#LyX 2.0 created this file. For more info see http://www.lyx.org/
\lyxformat 413
\begin_document
\begin_header
\textclass article
\use_default_options true
\maintain_unincluded_children false
\language russian
\language_package default
\inputencoding auto
\fontencoding global
\font_roman default
\font_sans default
\font_typewriter default
\font_default_family default
\use_non_tex_fonts false
\font_sc false
\font_osf false
\font_sf_scale 100
\font_tt_scale 100

\graphics default
\default_output_format default
\output_sync 0
\bibtex_command default
\index_command default
\paperfontsize default
\spacing single
\use_hyperref false
\papersize default
\use_geometry false
\use_amsmath 1
\use_esint 1
\use_mhchem 1
\use_mathdots 1
\cite_engine basic
\use_bibtopic false
\use_indices false
\paperorientation portrait
\suppress_date false
\use_refstyle 1
\index Index
\shortcut idx
\color #008000
\end_index
\secnumdepth 0
\tocdepth 3
\paragraph_separation skip
\defskip smallskip
\quotes_language english
\papercolumns 1
\papersides 1
\paperpagestyle default
\tracking_changes false
\output_changes false
\html_math_output 0
\html_css_as_file 0
\html_be_strict false
\end_header

\begin_body

\begin_layout Standard
\begin_inset CommandInset toc
LatexCommand tableofcontents

\end_inset


\end_layout

\begin_layout Standard
\begin_inset Newpage pagebreak
\end_inset


\end_layout

\begin_layout Section
Авторы и лицензия
\end_layout

\begin_layout Standard
Автор перевода: sorrymak (the_electric_hand), используются наработки village_gee
k (Павел Павленко, 
\begin_inset CommandInset href
LatexCommand href
target "https://twitter.com/village_geek"

\end_inset

).
 Отдельная благодарность Максиму Полетаеву (
\begin_inset CommandInset href
LatexCommand href
target "https://zenwalker.me/"

\end_inset

), Виктору Розаеву и другим авторам и переводчикам книг о языке программирования
 Go.
\end_layout

\begin_layout Standard
Лицензия: Creative Commons Attribution-ShareAlike (CC-BY-SA).
\end_layout

\begin_layout Section
Hello World
\end_layout

\begin_layout Standard
Нашей первой практикой в Go будет helloworld — программа, печатающая 
\begin_inset Quotes eld
\end_inset

hello world
\begin_inset Quotes erd
\end_inset

 (
\begin_inset Quotes eld
\end_inset

привет всем
\begin_inset Quotes erd
\end_inset

) в терминале.
 Ниже приведён её полный исходный код:
\end_layout

\begin_layout LyX-Code
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout
\noindent

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

    fmt.Println("hello world")
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout Standard
Чтобы выполнить эту программу, необходимо создать файл hello-world.go и выполнить
 команду 
\shape italic
go run
\shape default
:
\end_layout

\begin_layout LyX-Code
$ go run hello-world.go
\end_layout

\begin_layout LyX-Code
hello world
\end_layout

\begin_layout Standard
Если нам необходимо собрать программу в единый бинарный файл, то мы можем
 использовать команду 
\shape slanted
go build
\shape default
.
 Полученный файл можно просто запустить, как любой другой исполняемый: 
\end_layout

\begin_layout LyX-Code
$ go build hello-world.go
\end_layout

\begin_layout LyX-Code
$ ls 
\end_layout

\begin_layout LyX-Code
hello-world	hello-world.go
\end_layout

\begin_layout LyX-Code
$ ./hello-world
\end_layout

\begin_layout LyX-Code
hello world
\end_layout

\begin_layout Standard
Теперь, когда мы научились выполнять и собирать программы на Go, настало
 время приступить к изучению непосредственно самого языка.
\end_layout

\begin_layout Section
Значения
\end_layout

\begin_layout Standard
В языке программирования Go есть значения нескольких типов: строки, целые
 числа, числа с плавающей точкой (вещественные), логические значения.
 В коде ниже можно встретить несколько примеров:
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Строки.
 Соединяются (конкатенируются) оператором +
\end_layout

\begin_layout Plain Layout

	fmt.Println("go" + "lang")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Целые числа и числа с плавающей точкой
\end_layout

\begin_layout Plain Layout

	fmt.Println("1+1 =", 1+1) 
\end_layout

\begin_layout Plain Layout

	fmt.Println("7.0/3.0 =", 7.0/3.0)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Логические значения и логические операторы
\end_layout

\begin_layout Plain Layout

	fmt.Println(true && false) 
\end_layout

\begin_layout Plain Layout

	fmt.Println(true || false)
\end_layout

\begin_layout Plain Layout

	fmt.Println(!true)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run values.go
\end_layout

\begin_layout LyX-Code
golang
\end_layout

\begin_layout LyX-Code
1+1 = 2
\end_layout

\begin_layout LyX-Code
7.0/3.0 = 2.3333333333333335
\end_layout

\begin_layout LyX-Code
false
\end_layout

\begin_layout LyX-Code
true
\end_layout

\begin_layout LyX-Code
false
\end_layout

\begin_layout Section
Переменные
\end_layout

\begin_layout Standard
Все переменные в Go необходимо объявлять явным образом.
 Это используется компилятором, в частности, для проверки вызовов функций
 на корректность.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	var a string = "initial"
\end_layout

\begin_layout Plain Layout

	fmt.Println(a)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	var b, c int = 1, 2
\end_layout

\begin_layout Plain Layout

	fmt.Println(b, c)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	var d = true
\end_layout

\begin_layout Plain Layout

	fmt.Println(d)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	var e int
\end_layout

\begin_layout Plain Layout

	fmt.Println(e)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	f := "short"
\end_layout

\begin_layout Plain Layout

	fmt.Println(f)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run variables.go
\end_layout

\begin_layout LyX-Code
initial
\end_layout

\begin_layout LyX-Code
1 2
\end_layout

\begin_layout LyX-Code
true
\end_layout

\begin_layout LyX-Code
0
\end_layout

\begin_layout LyX-Code
short
\end_layout

\begin_layout Standard
С помощью ключевого слова 
\shape italic
var
\shape default
 можно объявить одну или сразу несколько переменных, при этом при использовании
 Go самостоятельно определит их тип.
 Переменные, которые были объявлены, но которые ещё не были инициализированы,
 имеют нулевое значение (zero-valued).
 Как видно на примере выше, переменная 
\shape italic
e
\shape default
 имеет значение 0.
\end_layout

\begin_layout Standard
Оператор 
\shape italic
:=
\shape default
 может служить заменой ключевому слову 
\shape italic
var
\shape default
.
 Так, выражение 
\family typewriter
f := "short" 
\family default
эквивалентно выражению 
\family typewriter
var f string = "short"
\family default
.
\end_layout

\begin_layout Section
Константы
\end_layout

\begin_layout Standard
В Go поддерживаются константы нескольких видов: символы, строки, логические
 и численные значения.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

import "math"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

const s string = "constant"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	
\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	fmt.Println(s)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	const n = 500000000
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	const d = 3e20 / n
\end_layout

\begin_layout Plain Layout

	fmt.Println(d)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	fmt.Println(int64(d))
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	fmt.Println(math.Sin(n))
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run constant.go 
\end_layout

\begin_layout LyX-Code
constant
\end_layout

\begin_layout LyX-Code
6e+11
\end_layout

\begin_layout LyX-Code
600000000000
\end_layout

\begin_layout LyX-Code
-0.28470407323754404
\end_layout

\begin_layout Standard
Константы объявляются с помощью ключевого слова 
\shape italic
const
\shape default
, и, как видно на примере выше, выражения с этим ключевым словом могут находитьс
я в любй части программы — так же, как и выражения с 
\shape italic
var
\shape default
.
\end_layout

\begin_layout Standard
Операции с константами — это так называемая арифметика произвольной точности,
 т.е.
 длина чисел ограничена только объёмом оперативной памяти.
\end_layout

\begin_layout Standard
У численной константы нет типа до тех пор, пока он не присвоен явным образом,
 например, так: 
\family typewriter
fmt.Println(int64(d))
\family default
.
 Также числу может быть присвоен тип в том случае, если этого требует контекст,
 в частности, в случае присвоения переменной или вызова функции.
 Так, в примере выше, 
\shape italic
math.Sin 
\shape default
имеет тип 
\shape italic
float64
\shape default
.
\end_layout

\begin_layout Section
For
\end_layout

\begin_layout Standard

\shape italic
for
\shape default
 — это конструкция для построения циклов.
 В отличие от других языков, в Go все циклы создаются только с помощью 
\shape italic
for
\shape default
, конструкции 
\shape italic
while
\shape default
 в Go нет.
 В примере ниже представлены все три основных типа циклов.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Основной тип -- for с одним условием
\end_layout

\begin_layout Plain Layout

	i := 1
\end_layout

\begin_layout Plain Layout

	for i <= 3 {
\end_layout

\begin_layout Plain Layout

		fmt.Println(i)
\end_layout

\begin_layout Plain Layout

		i = i + 1
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Классический тип -- начальное значение, условие и приращение
\end_layout

\begin_layout Plain Layout

	for j := 7; j <= 9; j++ {
\end_layout

\begin_layout Plain Layout

		fmt.Println(j)
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//for без условий будет продолжать цикл бесконечно; выйти из него
\end_layout

\begin_layout Plain Layout

	//можно с помощью ключевого слова break
\end_layout

\begin_layout Plain Layout

	for {
\end_layout

\begin_layout Plain Layout

	fmt.Println("loop")
\end_layout

\begin_layout Plain Layout

	break
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run for.go
\end_layout

\begin_layout LyX-Code
1
\end_layout

\begin_layout LyX-Code
2
\end_layout

\begin_layout LyX-Code
3
\end_layout

\begin_layout LyX-Code
7
\end_layout

\begin_layout LyX-Code
8
\end_layout

\begin_layout LyX-Code
9
\end_layout

\begin_layout LyX-Code
loop
\end_layout

\begin_layout Standard
Мы увидим прочие разновидности конструкции 
\shape italic
for
\shape default
 в следующих разделах, когда будем рассматривать диапазоны (ключевое слово
 
\shape italic
range
\shape default
), каналы и прочие структуры данных.
\end_layout

\begin_layout Section
If/else
\end_layout

\begin_layout Standard
Ветвление в Go осуществляется с помощью операторов 
\shape italic
if
\shape default
 и 
\shape italic
else
\shape default
.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Базовый пример
\end_layout

\begin_layout Plain Layout

	if 7%2 == 0 {
\end_layout

\begin_layout Plain Layout

		fmt.Println("7 is even")
\end_layout

\begin_layout Plain Layout

	} else {
\end_layout

\begin_layout Plain Layout

		fmt.Println("7 is odd")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//else -- необязательная часть выражения
\end_layout

\begin_layout Plain Layout

	if 8%4 == 0 {
\end_layout

\begin_layout Plain Layout

		fmt.Println("8 is divisible by 4")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Условию может предшествовать объявление, в таком случае
\end_layout

\begin_layout Plain Layout

	//эта переменная будет доступна во всех ответвлениях
\end_layout

\begin_layout Plain Layout

	if num := 9; num < 0 {
\end_layout

\begin_layout Plain Layout

		fmt.Println(num, "is negative")
\end_layout

\begin_layout Plain Layout

	} else if num < 10 {
\end_layout

\begin_layout Plain Layout

		fmt.Println(num, "has 1 digit")
\end_layout

\begin_layout Plain Layout

	} else {
\end_layout

\begin_layout Plain Layout

		fmt.Println(num, "has multiple digits")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run if-else.go 
\end_layout

\begin_layout LyX-Code
7 is odd
\end_layout

\begin_layout LyX-Code
8 is divisible by 4
\end_layout

\begin_layout LyX-Code
9 has 1 digit
\end_layout

\begin_layout Standard
Заметьте, что в Go, в отличие от других языков, условия не окружаются круглыми
 скобками, но при этом в конструкции всё равно необходимо использовать скобки
 фигурные.
\end_layout

\begin_layout Standard
В языке программирования Go нет так называемого тернарного оператора, вместо
 него необходимо использовать описанную в этом разделе конструкцию if/else.
\end_layout

\begin_layout Section
Switch
\end_layout

\begin_layout Standard
Оператор 
\shape italic
switch
\shape default
 предназначен для множественного ветвления.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

import "time"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Базовый switch
\end_layout

\begin_layout Plain Layout

	i := 2
\end_layout

\begin_layout Plain Layout

	fmt.Print("write ", i, " as ")
\end_layout

\begin_layout Plain Layout

	switch i {
\end_layout

\begin_layout Plain Layout

	case 1:
\end_layout

\begin_layout Plain Layout

		fmt.Println("one")
\end_layout

\begin_layout Plain Layout

	case 2:
\end_layout

\begin_layout Plain Layout

		fmt.Println("two")
\end_layout

\begin_layout Plain Layout

	case 3:
\end_layout

\begin_layout Plain Layout

		fmt.Println("three")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//В одном и том же выражении case может быть одновременно
\end_layout

\begin_layout Plain Layout

	//несколько условий, которые должны быть разделены запятой.
\end_layout

\begin_layout Plain Layout

	//Также в этот пример мы включили необязательный раздел default,
\end_layout

\begin_layout Plain Layout

	//т.е.
 значение по умолчанию
\end_layout

\begin_layout Plain Layout

	switch time.Now().Weekday() {
\end_layout

\begin_layout Plain Layout

	case time.Saturday, time.Sunday:
\end_layout

\begin_layout Plain Layout

		fmt.Println("it's the weekend")
\end_layout

\begin_layout Plain Layout

	default:
\end_layout

\begin_layout Plain Layout

		fmt.Println("it's a weekday")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//switch без следующего за ним выражения можно использовать как
\end_layout

\begin_layout Plain Layout

	//замену операторам if/else.
 Из этого примера также видно, что
\end_layout

\begin_layout Plain Layout

	//условия case не обязательно должны быть константами
\end_layout

\begin_layout Plain Layout

	t := time.Now()
\end_layout

\begin_layout Plain Layout

	switch {
\end_layout

\begin_layout Plain Layout

	case t.Hour() < 12:
\end_layout

\begin_layout Plain Layout

		fmt.Println("it's before noon")
\end_layout

\begin_layout Plain Layout

	default:
\end_layout

\begin_layout Plain Layout

		fmt.Println("it's after noon")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run switch.go 
\end_layout

\begin_layout LyX-Code
write 2 as two
\end_layout

\begin_layout LyX-Code
it's the weekend
\end_layout

\begin_layout LyX-Code
it's before noon
\end_layout

\begin_layout Section
Массивы
\end_layout

\begin_layout Standard
Массивы в Go — это нумерованная последовательность элементов, для которой
 задана определённая длина.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Здесь мы создаём массив из 5 целочисленных элементов.
 Тип
\end_layout

\begin_layout Plain Layout

	//элементов массива и их количество вместе образуют тип массива.
\end_layout

\begin_layout Plain Layout

	//По умолчанию массив заполнен нулевыми значениями (zero-valued)
\end_layout

\begin_layout Plain Layout

	var a [5]int
\end_layout

\begin_layout Plain Layout

	fmt.Println("emp:", a)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Мы можем присвоить определённому элементу массива значение
\end_layout

\begin_layout Plain Layout

	//и в дальнейшем использовать его таким образом, как на примере ниже
\end_layout

\begin_layout Plain Layout

	a[4] = 100
\end_layout

\begin_layout Plain Layout

	fmt.Println("set:", a)
\end_layout

\begin_layout Plain Layout

	fmt.Println("get:", a[4])
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Функция len возвращает длину массива
\end_layout

\begin_layout Plain Layout

	fmt.Println("len:", len(a))
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Таким образом можно объявить массив и присвоить ему значения
\end_layout

\begin_layout Plain Layout

	//в одну строчку
\end_layout

\begin_layout Plain Layout

	b := [5]int{1, 2, 3, 4, 5}
\end_layout

\begin_layout Plain Layout

	fmt.Println("dcl:", b)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Массивы могут быть многомерными
\end_layout

\begin_layout Plain Layout

	var twoD [2][3]int
\end_layout

\begin_layout Plain Layout

	for i := 0; i < 2; i++ {
\end_layout

\begin_layout Plain Layout

		for j := 0; j < 3; j++ {
\end_layout

\begin_layout Plain Layout

			twoD[i][j] = i + j
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	fmt.Println("2d: ", twoD)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run arrays.go
\end_layout

\begin_layout LyX-Code
emp: [0 0 0 0 0]
\end_layout

\begin_layout LyX-Code
set: [0 0 0 0 100]
\end_layout

\begin_layout LyX-Code
get: 100
\end_layout

\begin_layout LyX-Code
len: 5
\end_layout

\begin_layout LyX-Code
dcl: [1 2 3 4 5]
\end_layout

\begin_layout LyX-Code
2d: [[0 1 2] [1 2 3]]
\end_layout

\begin_layout Standard
Стоит отметить, что при выводе массивов с помощью функции 
\shape italic
fmt.Println
\shape default
 они появляются в форме [знач1 знач2 знач3 ...].
\end_layout

\begin_layout Standard
В следующем разделе мы рассмотрим срезы, которые как правило используются
 чаще, чем массивы.
\end_layout

\begin_layout Section
Срезы
\end_layout

\begin_layout Standard
Срезы — это одна из ключевых структур данных в Go, которая даёт бо́льшую
 гибкость в сравнении с массивами.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//В отличие от массивов, тип срезов определяется элементами,
\end_layout

\begin_layout Plain Layout

	//которые они содержат, а не их количеством.
 Чтобы создать
\end_layout

\begin_layout Plain Layout

	//срез с ненулевой длиной, используется встроенная функция make.
\end_layout

\begin_layout Plain Layout

	//В примере ниже мы создаём срез строк (strings) длиной в 3 элемента
\end_layout

\begin_layout Plain Layout

	//(которым изначально присвоены нулевые значения (zero-valued))
\end_layout

\begin_layout Plain Layout

	s := make([]string, 3)
\end_layout

\begin_layout Plain Layout

	fmt.Println("emp:", s)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Мы можем присваивать и получать значения так же как и с массивами
\end_layout

\begin_layout Plain Layout

	s[0] = "a"
\end_layout

\begin_layout Plain Layout

	s[1] = "b"
\end_layout

\begin_layout Plain Layout

	s[2] = "c"
\end_layout

\begin_layout Plain Layout

	fmt.Println("set:", s)
\end_layout

\begin_layout Plain Layout

	fmt.Println("get:", s[2])
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Функция len возвращает длину среза
\end_layout

\begin_layout Plain Layout

	fmt.Println("len:", len(s))
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//В дополнение к этому, срезы позволяют выполнять операции, недоступные
\end_layout

\begin_layout Plain Layout

	//при работе с массивами.
 К примеру, функция append возвращает срез с
\end_layout

\begin_layout Plain Layout

	//новыми элементами.
 Заметьте, что необходимо сохранить значение, возвращённое
\end_layout

\begin_layout Plain Layout

	//append, например, в той же переменной
\end_layout

\begin_layout Plain Layout

	s = append(s, "d")
\end_layout

\begin_layout Plain Layout

	s = append(s, "e", "f")
\end_layout

\begin_layout Plain Layout

	fmt.Println("apd:", s)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Срезы можно копировать.
 В этом примере мы создаём пустой срез той же длины,
\end_layout

\begin_layout Plain Layout

	//что и s, и копируем s в c
\end_layout

\begin_layout Plain Layout

	c := make([]string, len(s))
\end_layout

\begin_layout Plain Layout

	copy(c, s)
\end_layout

\begin_layout Plain Layout

	fmt.Println("cpy:", c)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Срезы поддерживают оператор с синтаксисом срез[нижн:верхн].
 В примере ниже
\end_layout

\begin_layout Plain Layout

	//переменной l присваивается срез с элементами s[2], s[3] и s[4]
\end_layout

\begin_layout Plain Layout

	l := s[2:5]
\end_layout

\begin_layout Plain Layout

	fmt.Println("sl1:", l)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Этот срез содержит элементы, предшествующие s[5] (но не включает непосредстве
нно s[5])
\end_layout

\begin_layout Plain Layout

	l = s[:5]
\end_layout

\begin_layout Plain Layout

	fmt.Println("sl2:", l)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Этот срез содержит элементы, следующие за s[2] (и включает в себя непосредств
енно s[2])
\end_layout

\begin_layout Plain Layout

	l = s[2:]
\end_layout

\begin_layout Plain Layout

	fmt.Println("sl3:", l)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Мы можем инициализировать срез и присвоить ему значения в одну строчку
\end_layout

\begin_layout Plain Layout

	t := []string{"g", "h", "i"}
\end_layout

\begin_layout Plain Layout

	fmt.Println("dcl:", t)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Из срезов можно составить многомерные структуры данных.
 Длина срезов нижнего уровня
\end_layout

\begin_layout Plain Layout

	//может варьироваться, в отличие от случаев с многомерными массивами
\end_layout

\begin_layout Plain Layout

	twoD := make([][]int, 3)
\end_layout

\begin_layout Plain Layout

	for i := 0; i < 3; i++ {
\end_layout

\begin_layout Plain Layout

		innerLen := i + 1
\end_layout

\begin_layout Plain Layout

		twoD[i] = make([]int, innerLen)
\end_layout

\begin_layout Plain Layout

		for j := 0; j < innerLen; j++ {
\end_layout

\begin_layout Plain Layout

			twoD[i][j] = i + j
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	fmt.Println("2d: ", twoD)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run slices.go
\end_layout

\begin_layout LyX-Code
emp: [ ]
\end_layout

\begin_layout LyX-Code
set: [a b c]
\end_layout

\begin_layout LyX-Code
get: c
\end_layout

\begin_layout LyX-Code
len: 3
\end_layout

\begin_layout LyX-Code
apd: [a b c d e f]
\end_layout

\begin_layout LyX-Code
cpy: [a b c d e f]
\end_layout

\begin_layout LyX-Code
sl1: [c d e]
\end_layout

\begin_layout LyX-Code
sl2: [a b c d e]
\end_layout

\begin_layout LyX-Code
sl3: [c d e f]
\end_layout

\begin_layout LyX-Code
dcl: [g h i]
\end_layout

\begin_layout LyX-Code
2d: [[0] [1 2] [2 3 4]]
\end_layout

\begin_layout Standard
Несмотря на то, что срезы серьёзно отличаются от массивов, при их выводе
 с помощью функции 
\shape italic
fmt.Println
\shape default
 они появляются в той же форме — [знач1 знач2 знач3 ...].
\end_layout

\begin_layout Standard
Если вы хотите узнать больше о внутреннем устройстве срезов, обратите внимание
 на этот пост в блоге разработчиков языка программирования Go: 
\begin_inset CommandInset href
LatexCommand href
target "https://blog.golang.org/go-slices-usage-and-internals"

\end_inset

 (англ.).
\end_layout

\begin_layout Standard
В следующем разделе мы рассмотрим ещё одну структуру данных, а именно —
 хеши.
\end_layout

\begin_layout Section
Хеши
\end_layout

\begin_layout Standard
Хеши — это ассоциативная структура данных (
\begin_inset CommandInset href
LatexCommand href
target "https://ru.wikipedia.org/wiki/Ассоциативный_массив"

\end_inset

), иногда называемая картами или словарями.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Чтобы создать пустой хеш, используется функция make.
 Синтаксис:
\end_layout

\begin_layout Plain Layout

	//make(map[тип-ключей]тип-значений)
\end_layout

\begin_layout Plain Layout

	m := make(map[string]int)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Задавать пары ключ/значение можно с помощью выражения имя-хеша[ключ]
 = значение
\end_layout

\begin_layout Plain Layout

	m["k1"] = 7
\end_layout

\begin_layout Plain Layout

	m["k2"] = 13
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//При выводе хеша с помощью функции fmt.Println будут напечатаны все пары
 ключ/значение
\end_layout

\begin_layout Plain Layout

	fmt.Println("map:", m)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Получить значение, соответствующее ключу, можно с помощью выражения имя-хеша[
ключ]
\end_layout

\begin_layout Plain Layout

	v1 := m["k1"]
\end_layout

\begin_layout Plain Layout

	fmt.Println("v1: ", v1)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Функция len возвращает количество пар ключ/значение
\end_layout

\begin_layout Plain Layout

	fmt.Println("len:", len(m))
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Функция delete удаляет пару ключ/значение из хеша
\end_layout

\begin_layout Plain Layout

	delete(m, "k2")
\end_layout

\begin_layout Plain Layout

	fmt.Println("map:", m)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//При получении значения, соответствующего ключу, возвращается также ещё
 одно значение,
\end_layout

\begin_layout Plain Layout

	//которое показывает, присутствует ли этот ключ в хеше.
 Эта возможность может быть
\end_layout

\begin_layout Plain Layout

	//использована для того, чтобы отделить отсутствующие ключи от ключей с
 нулевым значением.
\end_layout

\begin_layout Plain Layout

	//В примере ниже нам не нужно непосредственно основное значение, поэтому
 мы используем пустой
\end_layout

\begin_layout Plain Layout

	//идентификатор _
\end_layout

\begin_layout Plain Layout

	_, prs := m["k2"]
\end_layout

\begin_layout Plain Layout

	fmt.Println("prs:", prs)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Инициализировать хеш и присвоить ему значения можно в одну строчку
\end_layout

\begin_layout Plain Layout

	n := map[string]int{"foo": 1, "bar": 2}
\end_layout

\begin_layout Plain Layout

	fmt.Println("map:", n)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run maps.go 
\end_layout

\begin_layout LyX-Code
map: map[k1:7 k2:13]
\end_layout

\begin_layout LyX-Code
v1: 7
\end_layout

\begin_layout LyX-Code
len: 2
\end_layout

\begin_layout LyX-Code
map: map[k1:7]
\end_layout

\begin_layout LyX-Code
prs: false
\end_layout

\begin_layout LyX-Code
map: map[foo:1 bar:2]
\end_layout

\begin_layout Standard
При выводе хешей с помощью функции 
\shape italic
fmt.Println 
\shape default
они появляются в форме [ключ:знач ключ:знач].
\end_layout

\begin_layout Section
Range
\end_layout

\begin_layout Standard
С помощью ключевого слова range можно обойти (iterate) все элементы какой-либо
 структуры данных.
 На примерах ниже мы покажем, как использовать range со срезами, массивами,
 хешами и строками.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Здесь мы используем range, чтобы сложить все числа в срезе.
\end_layout

\begin_layout Plain Layout

	//Работать с массивами можно аналогичным образом
\end_layout

\begin_layout Plain Layout

	nums := []int{2, 3, 4}
\end_layout

\begin_layout Plain Layout

	sum := 0
\end_layout

\begin_layout Plain Layout

	for _, num := range nums {
\end_layout

\begin_layout Plain Layout

		sum += num
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	fmt.Println("sum:", sum)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//При использовании с массивами и срезами, range возваращает как
\end_layout

\begin_layout Plain Layout

	//индекс, так и значение элемента.
 В примере выше индекс нам был
\end_layout

\begin_layout Plain Layout

	//не нужен, поэтому мы использовали пустой идентификатор _, но
\end_layout

\begin_layout Plain Layout

	//иногда нам необходимо использовать и индекс тоже
\end_layout

\begin_layout Plain Layout

	for i, num := range nums {
\end_layout

\begin_layout Plain Layout

		if num == 3 {
\end_layout

\begin_layout Plain Layout

			fmt.Println("index:", i)
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//При использовании с хешами range обходит (iterate) пары ключ/значение
\end_layout

\begin_layout Plain Layout

	kvs := map[string]string{"a": "apple", "b": "banana"}
\end_layout

\begin_layout Plain Layout

	for k, v := range kvs {
\end_layout

\begin_layout Plain Layout

		fmt.Printf("%s -> %s
\backslash
n", k, v)
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//При использовании со строками range обходит кодовые точки (code
\end_layout

\begin_layout Plain Layout

	//points).
 Первое возвращаемое значение это стартовый байтовый
\end_layout

\begin_layout Plain Layout

	//индекс "руны"(прим.
 пер.: "руна" в Go -- это целочисленное
\end_layout

\begin_layout Plain Layout

	//значение, присвоенное кодовой точке), второе -- непосредственно
\end_layout

\begin_layout Plain Layout

	//"руна"
\end_layout

\begin_layout Plain Layout

	for i, c := range "go" {
\end_layout

\begin_layout Plain Layout

		fmt.Println(i, c)
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run range.go 
\end_layout

\begin_layout LyX-Code
sum: 9
\end_layout

\begin_layout LyX-Code
index: 1
\end_layout

\begin_layout LyX-Code
a -> apple
\end_layout

\begin_layout LyX-Code
b -> banana
\end_layout

\begin_layout LyX-Code
0 103
\end_layout

\begin_layout LyX-Code
1 111
\end_layout

\begin_layout Standard
\begin_inset Note Note
status open

\begin_layout Plain Layout
Слишком сумбурно.
 Переписать.
 Формат поправить.
\end_layout

\end_inset


\end_layout

\begin_layout Section
Функции
\end_layout

\begin_layout Standard
Функции — один из главных элементов в языке программирования Go.
 Как и ранее в этой книге, мы рассмотрим функции на нескольких примерах.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Функция, которая принимает в качестве аргументов два целых числа (int)
\end_layout

\begin_layout Plain Layout

//и возвращает их сумму, которая так же является целым числом (int)
\end_layout

\begin_layout Plain Layout

func plus(a int, b int) int {
\end_layout

\begin_layout Plain Layout

	//В отличие от некоторых других языков, Go не возвращает автоматически
\end_layout

\begin_layout Plain Layout

	//результат последнего выражения -- это приходится делать явно
\end_layout

\begin_layout Plain Layout

	return a + b
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Когда у функции есть несколько параметров одинакового типа, можно указать
 их тип
\end_layout

\begin_layout Plain Layout

//один раз, после последнего параметра этого типа
\end_layout

\begin_layout Plain Layout

func plusPlus(a, b, c int) int {
\end_layout

\begin_layout Plain Layout

	return a + b + c
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Функция вызывается следующим образом: имя-функции(аргументы)
\end_layout

\begin_layout Plain Layout

	res := plus(1, 2)
\end_layout

\begin_layout Plain Layout

	fmt.Println("1+2 =", res)
\end_layout

\begin_layout Plain Layout

	res = plusPlus(1, 2, 3)
\end_layout

\begin_layout Plain Layout

	fmt.Println("1+2+3 =", res)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run functions.go 
\end_layout

\begin_layout LyX-Code
1+2 = 3
\end_layout

\begin_layout LyX-Code
1+2+3 = 6
\end_layout

\begin_layout Standard
Функции в Go поддерживают ещё несколько возможностей, одна из которых —
 возможность возвращения нескольких значений, которую мы будем рассматривать
 в следующем разделе.
\end_layout

\begin_layout Section
Возврат нескольких значений
\end_layout

\begin_layout Standard
Go поддерживает возможность возврата нескольких значений, которая часто
 используется, чтобы, например, возвратить из функции как непосредственно
 результат её работы, так и ошибку.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//(int, int) означает, что эта функция возвращает 2 целых числа
\end_layout

\begin_layout Plain Layout

func vals() (int, int) {
\end_layout

\begin_layout Plain Layout

	return 3, 7
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//В этой части программы мы используем множественное присваивание,
\end_layout

\begin_layout Plain Layout

	//чтобы использовать значения из функции
\end_layout

\begin_layout Plain Layout

	a, b := vals()
\end_layout

\begin_layout Plain Layout

	fmt.Println(a)
\end_layout

\begin_layout Plain Layout

	fmt.Println(b)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Если нам нужны не все возвращаемые значения, то можно
\end_layout

\begin_layout Plain Layout

	//использовать пустой идентификатор _
\end_layout

\begin_layout Plain Layout

	_, c := vals()
\end_layout

\begin_layout Plain Layout

	fmt.Println(c)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run multiple-return-values.go
\end_layout

\begin_layout LyX-Code
3
\end_layout

\begin_layout LyX-Code
7
\end_layout

\begin_layout LyX-Code
7
\end_layout

\begin_layout Standard
Другая полезная возможность в Go — это то, что функции могут принимать произволь
ное количество аргументов.
 Об этом мы поговорим в следующем разделе.
\end_layout

\begin_layout Section
Вариативные функции
\end_layout

\begin_layout Standard
Вариативные функции (
\begin_inset CommandInset href
LatexCommand href
target "https://ru.wikipedia.org/wiki/Вариативная_функция"

\end_inset

) — это функции, которые принимают произвольное число аргументов.
 Примером вариативной функции является 
\shape italic
fmt.Println
\shape default
.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Функция sum принимает в качестве аргументов произвольное число целых чисел
\end_layout

\begin_layout Plain Layout

func sum(nums ...int) {
\end_layout

\begin_layout Plain Layout

	fmt.Print(nums, " ")
\end_layout

\begin_layout Plain Layout

	total := 0
\end_layout

\begin_layout Plain Layout

	for _, num := range nums {
\end_layout

\begin_layout Plain Layout

		total += num
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	fmt.Println(total)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Вариативные функции вызываются так же, как и любые другие
\end_layout

\begin_layout Plain Layout

	sum(1, 2)
\end_layout

\begin_layout Plain Layout

	sum(1, 2, 3)
\end_layout

\begin_layout Plain Layout

	
\end_layout

\begin_layout Plain Layout

	//Вариативные функции могут принимать в качестве аругмента срезы, в данном
\end_layout

\begin_layout Plain Layout

	//случае -- срез с целыми числами.
 Делается это так: функ(срез...)
\end_layout

\begin_layout Plain Layout

	nums := []int{1, 2, 3, 4}
\end_layout

\begin_layout Plain Layout

	sum(nums...)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run variadic-functions.go 
\end_layout

\begin_layout LyX-Code
[1 2] 3
\end_layout

\begin_layout LyX-Code
[1 2 3] 6
\end_layout

\begin_layout LyX-Code
[1 2 3 4] 10
\end_layout

\begin_layout Standard
Ещё одна примечательная возможность функций в Go — возможность формировать
 замыкания, которые будут рассмотрены в следующем разделе.
\end_layout

\begin_layout Section
Замыкания
\end_layout

\begin_layout Standard
Язык программирования Go поддерживает анонимные функции (
\begin_inset CommandInset href
LatexCommand href
target "https://ru.wikipedia.org/wiki/Анонимная_функция"

\end_inset

), которые могут использоваться для формирования замыканий (
\begin_inset CommandInset href
LatexCommand href
target "https://ru.wikipedia.org/wiki/Замыкание_(программирование)"

\end_inset

).
 Анонимные функции бывают полезны, когда нужно 
\begin_inset Quotes eld
\end_inset

завернуть
\begin_inset Quotes erd
\end_inset

 код в функцию, не присваивая ей имени.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Функция intSeq возвращает другую функцию, которую мы анонимно объявляем
\end_layout

\begin_layout Plain Layout

//в теле функции intSeq.
 Возвращаемая функция замыкает переменную i, образуя
\end_layout

\begin_layout Plain Layout

//замыкание
\end_layout

\begin_layout Plain Layout

func intSeq() func() int {
\end_layout

\begin_layout Plain Layout

	i := 0
\end_layout

\begin_layout Plain Layout

	return func() int {
\end_layout

\begin_layout Plain Layout

		i += 1
\end_layout

\begin_layout Plain Layout

		return i
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Мы присваиваем значение (возвращаемую функцию) intSeq переменной nextInt.
\end_layout

\begin_layout Plain Layout

	//Эта функция захватывает переменную i, которая будет обновляться каждый
 раз,
\end_layout

\begin_layout Plain Layout

	//когда мы вызываем nextInt
\end_layout

\begin_layout Plain Layout

	nextInt := intSeq()
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Увидеть эффект от замыкания можно, вызвав nextInt несколько раз
\end_layout

\begin_layout Plain Layout

	fmt.Println(nextInt())
\end_layout

\begin_layout Plain Layout

	fmt.Println(nextInt())
\end_layout

\begin_layout Plain Layout

	fmt.Println(nextInt())
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Чтобы убедиться, что значение переменной i уникально для каждой функции,
\end_layout

\begin_layout Plain Layout

	//создадим ещё одну
\end_layout

\begin_layout Plain Layout

	newInts := intSeq()
\end_layout

\begin_layout Plain Layout

	fmt.Println(newInts())
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run closures.go
\end_layout

\begin_layout LyX-Code
1
\end_layout

\begin_layout LyX-Code
2
\end_layout

\begin_layout LyX-Code
3
\end_layout

\begin_layout LyX-Code
1
\end_layout

\begin_layout Standard
В следующем разделе мы рассмотрим ещё одну возможность функций — рекурсию.
\end_layout

\begin_layout Section
Рекурсия
\end_layout

\begin_layout Standard
Go поддерживает рекурсивные функции (
\begin_inset CommandInset href
LatexCommand href
target "https://ru.wikipedia.org/wiki/Рекурсивная_функция"

\end_inset

).
 Ниже мы приводим классический пример с факториалом.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func fact(n int) int {
\end_layout

\begin_layout Plain Layout

	if n == 0 {
\end_layout

\begin_layout Plain Layout

		return 1
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	return n * fact(n-1)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	fmt.Println(fact(7))
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run recursion.go 
\end_layout

\begin_layout LyX-Code
5040
\end_layout

\begin_layout Standard
Функция 
\shape italic
fact
\shape default
 вызывает саму себя до тех пор, пока наконец не достигнет значения 
\shape italic
fact(0)
\shape default
.
\end_layout

\begin_layout Section
Указатели
\end_layout

\begin_layout Standard
\begin_inset Note Note
status open

\begin_layout Plain Layout
Полностью переписать!
\end_layout

\end_inset


\end_layout

\begin_layout Standard
Go поддерживает указатели (
\begin_inset CommandInset href
LatexCommand href
target "https://ru.wikipedia.org/wiki/Указатель_(тип_данных)"

\end_inset

), позволяющие передавать адреса ячеек памяти.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func zeroval(ival int) {
\end_layout

\begin_layout Plain Layout

	ival = 0
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func zeroptr(iptr *int) {
\end_layout

\begin_layout Plain Layout

	*iptr = 0
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	i := 1
\end_layout

\begin_layout Plain Layout

	fmt.Println("initial:", i)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	zeroval(i)
\end_layout

\begin_layout Plain Layout

	fmt.Println("zeroval:", i)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	zeroptr(&i)
\end_layout

\begin_layout Plain Layout

	fmt.Println("zeroptr:", i)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	fmt.Println("pointer:", &i)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run pointers.go
\end_layout

\begin_layout LyX-Code
initial: 1
\end_layout

\begin_layout LyX-Code
zeroval: 1
\end_layout

\begin_layout LyX-Code
zeroptr: 0
\end_layout

\begin_layout LyX-Code
pointer: 0x42131100
\end_layout

\begin_layout Standard
Мы объясним принципы работы указателей на примере двух функций, 
\shape italic
zeroval
\shape default
 и 
\shape italic
zeroptr
\shape default
.
 У функции 
\shape italic
zeroval
\shape default
 есть целочисленный (
\shape italic
int
\shape default
) параметр, поэтому аргумент будет передан по значению (по умолчанию).
 
\shape italic
zeroval
\shape default
 получит копию значения, переданного через вызов функции.
\end_layout

\begin_layout Standard
У 
\shape italic
zeroptr же
\shape default
, в отличие от 
\shape italic
zeroval
\shape default
, есть параметр 
\shape italic
*int
\shape default
, что означает, что эта функция принимает в качестве аргумента целочисленный
 указатель.
 Далее в функции происходит так называемое разыменование указателя, после
 чего ему присваивается новое значение, что приводит к изменению значения
 в ячейке памяти.
\end_layout

\begin_layout Standard
С помощью синтаксиса 
\shape italic
&i
\shape default
 можно получить адрес ячейки памяти i, т.е.
 указатель на 
\shape italic
i
\shape default
.
\end_layout

\begin_layout Standard
Указатели тоже можно вывести на печать, как видно на примере выше.
\end_layout

\begin_layout Standard

\shape italic
zeroval
\shape default
 не меняет значение 
\shape italic
i
\shape default
 в функции 
\shape italic
main
\shape default
, но 
\shape italic
zeroptr
\shape default
 меняет, потому что обращается к значения в памяти для этой переменной.
\end_layout

\begin_layout Section
Структуры
\end_layout

\begin_layout Standard
Структуры — это группы полей, каждое из которых имеет свой тип.
 Они бывают полезны, когда необходимо сгруппировать данные в удобной форме.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Тип структуры person состоит из двух полей, name и age
\end_layout

\begin_layout Plain Layout

type person struct {
\end_layout

\begin_layout Plain Layout

	name string
\end_layout

\begin_layout Plain Layout

	age  int
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Так создаётся новая структура 
\end_layout

\begin_layout Plain Layout

	fmt.Println(person{"Bob", 20})
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//При инициализации структуры можно указать названия полей
\end_layout

\begin_layout Plain Layout

	fmt.Println(person{name: "Alice", age: 30})
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Пропущенные при инициализации структуры поля будут иметь
\end_layout

\begin_layout Plain Layout

	//нулевое значение (zero-valued)
\end_layout

\begin_layout Plain Layout

	fmt.Println(person{name: "Fred"})
\end_layout

\begin_layout Plain Layout

	
\end_layout

\begin_layout Plain Layout

	//Для передачи указателя используется &
\end_layout

\begin_layout Plain Layout

	fmt.Println(&person{name: "Ann", age: 40})
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Для получения доступа к полям используется следующий синтаксис
\end_layout

\begin_layout Plain Layout

	s := person{name: "Sean", age: 50}
\end_layout

\begin_layout Plain Layout

	fmt.Println(s.name)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Тот же синтаксис используется и для работы с указателями в структуре
\end_layout

\begin_layout Plain Layout

	sp := &s
\end_layout

\begin_layout Plain Layout

	fmt.Println(sp.age)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Структуры мутабельны
\end_layout

\begin_layout Plain Layout

	sp.age = 51
\end_layout

\begin_layout Plain Layout

	fmt.Println(sp.age)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run structs.go
\end_layout

\begin_layout LyX-Code
{Bob 20}
\end_layout

\begin_layout LyX-Code
{Alice 30}
\end_layout

\begin_layout LyX-Code
{Fred 0}
\end_layout

\begin_layout LyX-Code
&{Ann 40}
\end_layout

\begin_layout LyX-Code
Sean
\end_layout

\begin_layout LyX-Code
50
\end_layout

\begin_layout LyX-Code
51
\end_layout

\begin_layout Section
Методы
\end_layout

\begin_layout Standard
Методы — это особые функции, связанные со структурами (и не только).
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

type rect struct {
\end_layout

\begin_layout Plain Layout

	width, height int
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//У метода area есть так называемый "ресивер" с типом *rect
\end_layout

\begin_layout Plain Layout

func (r *rect) area() int {
\end_layout

\begin_layout Plain Layout

	return r.width * r.height
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Тип ресивера может быть либо указателем, либо простым значением.
\end_layout

\begin_layout Plain Layout

//Ниже -- пример ресивера с простым значением (value receiver)
\end_layout

\begin_layout Plain Layout

func (r rect) perim() int {
\end_layout

\begin_layout Plain Layout

	return 2*r.width + 2*r.height
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	r := rect{width: 10, height: 5}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Здесь мы вызываем два метода нашей структуры
\end_layout

\begin_layout Plain Layout

	fmt.Println("area: ", r.area())
\end_layout

\begin_layout Plain Layout

	fmt.Println("perim:", r.perim())
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//При вызове методов, в случае необходимости Go автоматически
\end_layout

\begin_layout Plain Layout

	//урегулирует вопрос использования указателей или простых
\end_layout

\begin_layout Plain Layout

	//значений.
 Использование указателей
\end_layout

\begin_layout Plain Layout

	//может быть полезно, если вы хотите избежать копирования	
\end_layout

\begin_layout Plain Layout

	//значения при вызове метода, или для того, чтобы позволить
\end_layout

\begin_layout Plain Layout

	//методам изменять значения структуры
\end_layout

\begin_layout Plain Layout

	rp := &r
\end_layout

\begin_layout Plain Layout

	fmt.Println("area: ", rp.area())
\end_layout

\begin_layout Plain Layout

	fmt.Println("perim:", rp.perim())
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run methods.go 
\end_layout

\begin_layout LyX-Code
area: 50
\end_layout

\begin_layout LyX-Code
perim: 30
\end_layout

\begin_layout LyX-Code
area: 50
\end_layout

\begin_layout LyX-Code
perim: 30
\end_layout

\begin_layout Standard
В следующем примере мы рассмотрим механизм, предназначенный для группирования
 связанных методов — интерфейсы.
\end_layout

\begin_layout Section
Интерфейсы
\end_layout

\begin_layout Standard
Интерфейсы — это группы сигнатур методов, имеющие своё имя.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

import "math"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Ниже представлен базовый интерфейс для геометрических фигур
\end_layout

\begin_layout Plain Layout

type geometry interface {
\end_layout

\begin_layout Plain Layout

	area() float64
\end_layout

\begin_layout Plain Layout

	perim() float64
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//В качестве примера мы создадим интерфейсы для типов rect
\end_layout

\begin_layout Plain Layout

//(rectangle, прямоугольник) и circle (круг)
\end_layout

\begin_layout Plain Layout

type rect struct {
\end_layout

\begin_layout Plain Layout

	width, height float64
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

type circle struct {
\end_layout

\begin_layout Plain Layout

	radius float64
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Чтобы создать интерфейс, необходимо создать все методы этого
\end_layout

\begin_layout Plain Layout

//интерфейса.
 Ниже мы реализуем интерфейс geometry для типа rect
\end_layout

\begin_layout Plain Layout

func (r rect) area() float64 {
\end_layout

\begin_layout Plain Layout

	return r.width * r.height
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

func (r rect) perim() float64 {
\end_layout

\begin_layout Plain Layout

	return 2*r.width + 2*r.height
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Теперь -- для типа circle
\end_layout

\begin_layout Plain Layout

func (c circle) area() float64 {
\end_layout

\begin_layout Plain Layout

	return math.Pi * c.radius * c.radius
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

func (c circle) perim() float64 {
\end_layout

\begin_layout Plain Layout

	return 2 * math.Pi * c.radius
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Если переменная имеет тип "интерфейс", то мы можем вызывать методы
\end_layout

\begin_layout Plain Layout

//из указанного интерфейса.
 Здесь мы видим функцию measure, которая
\end_layout

\begin_layout Plain Layout

//работает как с circle, так и с rect
\end_layout

\begin_layout Plain Layout

func measure(g geometry) {
\end_layout

\begin_layout Plain Layout

	fmt.Println(g)
\end_layout

\begin_layout Plain Layout

	fmt.Println(g.area())
\end_layout

\begin_layout Plain Layout

	fmt.Println(g.perim())
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	r := rect{width: 3, height: 4}
\end_layout

\begin_layout Plain Layout

	c := circle{radius: 5}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//У типа circle и типа rect есть общий интерфейс geometry, что
\end_layout

\begin_layout Plain Layout

	//позволяет нам использовать переменные этих типов в качестве
\end_layout

\begin_layout Plain Layout

	//аргументов к функции measure
\end_layout

\begin_layout Plain Layout

	measure(r)
\end_layout

\begin_layout Plain Layout

	measure(c)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run interfaces.go
\end_layout

\begin_layout LyX-Code
{3 4}
\end_layout

\begin_layout LyX-Code
12
\end_layout

\begin_layout LyX-Code
14
\end_layout

\begin_layout LyX-Code
{5}
\end_layout

\begin_layout LyX-Code
78.53981633974483
\end_layout

\begin_layout LyX-Code
31.41592653589793
\end_layout

\begin_layout Standard
Если вы хотите узнать больше об интерфейсах в Go, обратите внимание на эту
 статью: 
\begin_inset CommandInset href
LatexCommand href
target "http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go"

\end_inset

 (англ.).
\end_layout

\begin_layout Section
Ошибки
\end_layout

\begin_layout Standard
Для обработки ошибок в Go принято использовать отдельное возвращаемое значение,
 в отличие от, к примеру, Java или Ruby, в которых принято использовать
 механизм исключений, или C, в котором иногда для этого используется единственно
е значение.
 Используемые в Go методы позволяют легко увидеть, какие функции возвратили
 ошибки, и обработать их стандартными средствами языка.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "errors"
\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Ошибки возвращаются последними и имеют встроенный тип error
\end_layout

\begin_layout Plain Layout

func f1(arg int) (int, error) {
\end_layout

\begin_layout Plain Layout

	if arg == 42 {
\end_layout

\begin_layout Plain Layout

		//errors.New создаёт значение, указывающее на ошибку,
\end_layout

\begin_layout Plain Layout

		//с определённым сообщением
\end_layout

\begin_layout Plain Layout

		return -1, errors.New("can't work with 42")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	//nil в качестве последнего возвращаемого сообщения указывает
\end_layout

\begin_layout Plain Layout

	//на то, что ошибки не было
\end_layout

\begin_layout Plain Layout

	return arg + 3, nil
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Возможно использовать собственный тип вместо типа error, для этого
\end_layout

\begin_layout Plain Layout

//нужно реализовать метод Error() для этого типа.
 На примере ниже
\end_layout

\begin_layout Plain Layout

//мы создаём свой тип, для того, чтобы показать, что произошла ошибка
\end_layout

\begin_layout Plain Layout

//при работе с аргументом функции
\end_layout

\begin_layout Plain Layout

type argError struct {
\end_layout

\begin_layout Plain Layout

	arg  int
\end_layout

\begin_layout Plain Layout

	prob string
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func (e *argError) Error() string {
\end_layout

\begin_layout Plain Layout

	return fmt.Sprintf("%d - %s", e.arg, e.prob)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func f2(arg int) (int, error) {
\end_layout

\begin_layout Plain Layout

	if arg == 42 {
\end_layout

\begin_layout Plain Layout

		//В этом случае мы используем &argError для того, чтобы
\end_layout

\begin_layout Plain Layout

		//создать новую структуру с полями arg и prob
\end_layout

\begin_layout Plain Layout

		return -1, &argError{arg, "can't work with it"}
\end_layout

\begin_layout Plain Layout

    }
\end_layout

\begin_layout Plain Layout

    return arg + 3, nil
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Эти два цикла тестируют приведённые выше функции.
 Стоит
\end_layout

\begin_layout Plain Layout

	//отметить, что в этом примере используется короткая форма
\end_layout

\begin_layout Plain Layout

	//оператора if
\end_layout

\begin_layout Plain Layout

	for _, i := range []int{7, 42} {
\end_layout

\begin_layout Plain Layout

		if r, e := f1(i); e != nil {
\end_layout

\begin_layout Plain Layout

			fmt.Println("f1 failed:", e)
\end_layout

\begin_layout Plain Layout

		} else {
\end_layout

\begin_layout Plain Layout

			fmt.Println("f1 worked:", r)
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	for _, i := range []int{7, 42} {
\end_layout

\begin_layout Plain Layout

		if r, e := f2(i); e != nil {
\end_layout

\begin_layout Plain Layout

			fmt.Println("f2 failed:", e)
\end_layout

\begin_layout Plain Layout

		} else {
\end_layout

\begin_layout Plain Layout

			fmt.Println("f2 worked:", r)
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Если вам нужно использовать данные из возвращаемой структуры,
\end_layout

\begin_layout Plain Layout

	//то необходимо использовать так называемый "type assertion"
\end_layout

\begin_layout Plain Layout

	_, e := f2(42)
\end_layout

\begin_layout Plain Layout

	if ae, ok := e.(*argError); ok {
\end_layout

\begin_layout Plain Layout

		fmt.Println(ae.arg)
\end_layout

\begin_layout Plain Layout

		fmt.Println(ae.prob)
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run errors.go
\end_layout

\begin_layout LyX-Code
f1 worked: 10
\end_layout

\begin_layout LyX-Code
f1 failed: can't work with 42
\end_layout

\begin_layout LyX-Code
f2 worked: 10
\end_layout

\begin_layout LyX-Code
f2 failed: 42 - can't work with it
\end_layout

\begin_layout LyX-Code
42
\end_layout

\begin_layout LyX-Code
can't work with it
\end_layout

\begin_layout Standard
Чтобы узнать больше об обработке ошибок в Go, стоит обратить внимание на
 этот пост: 
\begin_inset CommandInset href
LatexCommand href
target "https://blog.golang.org/error-handling-and-go"

\end_inset

 (англ.).
\end_layout

\begin_layout Section
Горутины
\end_layout

\begin_layout Standard
Горутина — это легковесный поток выполнения (тред).
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func f(from string) {
\end_layout

\begin_layout Plain Layout

	for i := 0; i < 3; i++ {
\end_layout

\begin_layout Plain Layout

		fmt.Println(from, ":", i)
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Здесь мы вызываем функцию f() обычным образом, она
\end_layout

\begin_layout Plain Layout

	//выполняется синхронно
\end_layout

\begin_layout Plain Layout

	f("direct")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Чтобы запустить эту функцию в горутине, необходимо
\end_layout

\begin_layout Plain Layout

	//использовать ключевое слово go.
 Эта горутина будет
\end_layout

\begin_layout Plain Layout

	//выполняться параллельно с остальными
\end_layout

\begin_layout Plain Layout

	go f("goroutine")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Горутины могут работать и с анонимными функциями
\end_layout

\begin_layout Plain Layout

	go func(msg string) {
\end_layout

\begin_layout Plain Layout

		fmt.Println(msg)
\end_layout

\begin_layout Plain Layout

	}("going")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Два наших вызова функции выполняются асинхронно в
\end_layout

\begin_layout Plain Layout

	//разных горутинах, поэтому здесь нужно использовать
\end_layout

\begin_layout Plain Layout

	//fmt.Scanln, чтобы программа не завершилась досрочно
\end_layout

\begin_layout Plain Layout

	//(в нашем случае программа завершится только тогда,
\end_layout

\begin_layout Plain Layout

	//когда пользователь нажёмт клавишу Enter)
\end_layout

\begin_layout Plain Layout

	var input string
\end_layout

\begin_layout Plain Layout

	fmt.Scanln(&input)
\end_layout

\begin_layout Plain Layout

	fmt.Println("done")
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run goroutines.go
\end_layout

\begin_layout LyX-Code
direct : 0
\end_layout

\begin_layout LyX-Code
direct : 1
\end_layout

\begin_layout LyX-Code
direct : 2
\end_layout

\begin_layout LyX-Code
goroutine : 0
\end_layout

\begin_layout LyX-Code
going
\end_layout

\begin_layout LyX-Code
goroutine : 1
\end_layout

\begin_layout LyX-Code
goroutine : 2
\end_layout

\begin_layout LyX-Code
<enter>
\end_layout

\begin_layout LyX-Code
done
\end_layout

\begin_layout Standard
Когда мы запускаем эту программу, в начале мы видим вывод первого (блокирующего)
 вызова, затем чередующийся вывод двух горутин.
 Чередование означает, что эти горутины выполняются одновременно.
\end_layout

\begin_layout Standard
В следующем разделе мы рассмотрим каналы, которые являются дополнением к
 горутинам.
\end_layout

\begin_layout Section
Каналы
\end_layout

\begin_layout Standard
Каналы — это связующие звенья между одновременно выполняющимися горутинами.
 В каналы можно отправлять сообщения из одной горутины и получать их в другой.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Новый канал создаётся так: make(chan тип-значения).
\end_layout

\begin_layout Plain Layout

	//Тип каналов определяется типом значения, который
\end_layout

\begin_layout Plain Layout

	//они передают
\end_layout

\begin_layout Plain Layout

	messages := make(chan string)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Чтобы отправить какое-либо значение в канал,
\end_layout

\begin_layout Plain Layout

	//необходимо использовать оператор <-.
 В примере
\end_layout

\begin_layout Plain Layout

	//ниже мы отправляем из новой горутины
\end_layout

\begin_layout Plain Layout

	//строку "ping" в канал nessages, который мы
\end_layout

\begin_layout Plain Layout

	//создали выше
\end_layout

\begin_layout Plain Layout

	go func() { messages <- "ping" }()
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//С помощью синтаксиса <-имя-канала можно получить
\end_layout

\begin_layout Plain Layout

	//значение из канала.
 В этом примере мы получаем
\end_layout

\begin_layout Plain Layout

	//строку "ping" (которую мы отправили выше) и выводим
\end_layout

\begin_layout Plain Layout

	//её на экран
\end_layout

\begin_layout Plain Layout

	msg := <-messages
\end_layout

\begin_layout Plain Layout

	fmt.Println(msg)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run channels.go 
\end_layout

\begin_layout LyX-Code
ping
\end_layout

\begin_layout Standard
Во время выполнения программы сообщение 
\begin_inset Quotes eld
\end_inset

ping
\begin_inset Quotes erd
\end_inset

 успешно передаётся из одной горутины в другую (функция main сама по себе
 является горутиной) с помощью нашего канала.
\end_layout

\begin_layout Standard
По умолчанию сообщения отправляются и принимаются только в том случае, когда
 горутины готовы к отправке/приёму сообщения.
 Благодаря этому нам не нужно было использовать 
\shape italic
fmt.Scanln
\shape default
 как в прошлом примере.
\end_layout

\begin_layout Section
Буферизация каналов
\end_layout

\begin_layout Standard
По умолчанию каналы не буферизованы (unbuffered), что означает, что отправить
 в них сообщение (
\shape italic
имя-канала <-
\shape default
) можно лишь в том случае, когда принимающая горутина будет готова его принять
 (
\shape italic
<-имя-канала
\shape default
).
 Буферизованные каналы же, в свою очередь, могут принимать ограниченное
 количество сообщений даже в том случае, когда получателя ещё нет.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//В этом примере мы создаём канал с буфером, вмещающим
\end_layout

\begin_layout Plain Layout

	//до 2 значений
\end_layout

\begin_layout Plain Layout

	messages := make(chan string, 2)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Поскольку этот канал буферизован, мы можем отправить
\end_layout

\begin_layout Plain Layout

	//сообщения, не принимая их одновременно с этим
\end_layout

\begin_layout Plain Layout

	messages <- "buffered"
\end_layout

\begin_layout Plain Layout

	messages <- "channel"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//В дальнейшем мы можем принять эти сообщения как обычно
\end_layout

\begin_layout Plain Layout

	fmt.Println(<-messages)
\end_layout

\begin_layout Plain Layout

	fmt.Println(<-messages)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run channel-buffering.go 
\end_layout

\begin_layout LyX-Code
buffered
\end_layout

\begin_layout LyX-Code
channel
\end_layout

\begin_layout Section
Синхронизация каналов
\end_layout

\begin_layout Standard
Мы можем использовать каналы, чтобы синхронизировать выполнение горутин.
 В примере ниже мы увидим, как с помощью блокирующего приёма сообщения можно
 дождаться завершения горутины перед завершением программы.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

import "time"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Это функция, которую мы запустим в горутине.
\end_layout

\begin_layout Plain Layout

//Канал done будет использован для того, чтобы
\end_layout

\begin_layout Plain Layout

//оповестить другую горутину о том, что работа
\end_layout

\begin_layout Plain Layout

//этой функции выполнена
\end_layout

\begin_layout Plain Layout

func worker(done chan bool) {
\end_layout

\begin_layout Plain Layout

	fmt.Print("working...")
\end_layout

\begin_layout Plain Layout

	time.Sleep(time.Second)
\end_layout

\begin_layout Plain Layout

	fmt.Println("done")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Отправляем сообщение о завершении работы
\end_layout

\begin_layout Plain Layout

	done <- true
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Запускаем горутину worker и передаём ей канал,
\end_layout

\begin_layout Plain Layout

	//который будет использован для оповещения
\end_layout

\begin_layout Plain Layout

	done := make(chan bool, 1)
\end_layout

\begin_layout Plain Layout

	go worker(done)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Здесь мы блокируем выполнение программы до тех пор,
\end_layout

\begin_layout Plain Layout

	//пока мы не получим сообщение от worker
\end_layout

\begin_layout Plain Layout

	<-done
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run channel-synchronization.go 
\end_layout

\begin_layout LyX-Code
working...done 
\end_layout

\begin_layout Standard
Если мы уберём 
\shape italic
<-done 
\shape default
из программы, то программа завершится ещё до того, как запустится worker.
\end_layout

\begin_layout Section
Направления каналов
\end_layout

\begin_layout Standard
При использовании каналов в качестве параметров функции можно указать, будет
 ли канал предназначен только для отправки или только для получения значений.
 Это повышает типобезопасность программы.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Функция ping принимает канал только для отправки
\end_layout

\begin_layout Plain Layout

//значений.
 Попытка приёма в этом канале вызовет
\end_layout

\begin_layout Plain Layout

//ошибку компиляции
\end_layout

\begin_layout Plain Layout

func ping(pings chan<- string, msg string) {
\end_layout

\begin_layout Plain Layout

	pings <- msg
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Функция pong принимает один канал для приёма (pings)
\end_layout

\begin_layout Plain Layout

//и второй для отправки (pongs)
\end_layout

\begin_layout Plain Layout

func pong(pings <-chan string, pongs chan<- string) {
\end_layout

\begin_layout Plain Layout

	msg := <-pings
\end_layout

\begin_layout Plain Layout

	pongs <- msg
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	pings := make(chan string, 1)
\end_layout

\begin_layout Plain Layout

	pongs := make(chan string, 1)
\end_layout

\begin_layout Plain Layout

	ping(pings, "passed message")
\end_layout

\begin_layout Plain Layout

	pong(pings, pongs)
\end_layout

\begin_layout Plain Layout

	fmt.Println(<-pongs)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run channel-directions.go
\end_layout

\begin_layout LyX-Code
passed message
\end_layout

\begin_layout Section
Select
\end_layout

\begin_layout Standard

\shape italic
select
\shape default
 позволяет ожидать выполнения многоканальных операций.
 Совместив горутины, каналы и оператор select, мы можем получить мощный
 и полезный инструмент.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "time"
\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//В нашем примере мы будем производить выбор из
\end_layout

\begin_layout Plain Layout

	//двух каналов
\end_layout

\begin_layout Plain Layout

	c1 := make(chan string)
\end_layout

\begin_layout Plain Layout

	c2 := make(chan string)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Каждый канал будет получать значение после
\end_layout

\begin_layout Plain Layout

	//некоторого промежутка времени, например,
\end_layout

\begin_layout Plain Layout

	//для имитации блокирования RPC операций в
\end_layout

\begin_layout Plain Layout

	//одновременно выполняющихся горутинах
\end_layout

\begin_layout Plain Layout

	go func() {
\end_layout

\begin_layout Plain Layout

		time.Sleep(time.Second * 1)
\end_layout

\begin_layout Plain Layout

		c1 <- "one"
\end_layout

\begin_layout Plain Layout

	}()
\end_layout

\begin_layout Plain Layout

	go func() {
\end_layout

\begin_layout Plain Layout

		time.Sleep(time.Second * 2)
\end_layout

\begin_layout Plain Layout

		c2 <- "two"
\end_layout

\begin_layout Plain Layout

	}()
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Мы будем использовать select для того, чтобы
\end_layout

\begin_layout Plain Layout

	//одновременно дождаться получения обоих значений
\end_layout

\begin_layout Plain Layout

	//и вывести их на экран по мере получения
\end_layout

\begin_layout Plain Layout

	for i := 0; i < 2; i++ {
\end_layout

\begin_layout Plain Layout

		select {
\end_layout

\begin_layout Plain Layout

		case msg1 := <-c1:
\end_layout

\begin_layout Plain Layout

			fmt.Println("received", msg1)
\end_layout

\begin_layout Plain Layout

		case msg2 := <-c2:
\end_layout

\begin_layout Plain Layout

			fmt.Println("received", msg2)
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ time go run select.go 
\end_layout

\begin_layout LyX-Code
received one
\end_layout

\begin_layout LyX-Code
received two
\end_layout

\begin_layout LyX-Code
real 0m2.245s
\end_layout

\begin_layout Standard
Как и ожидалось, мы получили сообщения 
\begin_inset Quotes eld
\end_inset

one
\begin_inset Quotes erd
\end_inset

 (
\begin_inset Quotes eld
\end_inset

один
\begin_inset Quotes erd
\end_inset

) и 
\begin_inset Quotes eld
\end_inset

two
\begin_inset Quotes erd
\end_inset

 (
\begin_inset Quotes eld
\end_inset

два
\begin_inset Quotes erd
\end_inset

).
\end_layout

\begin_layout Standard
Стоит отметить, что общее время выполнения составило всего около двух секунд,
 так как команды 
\shape italic
Sleep
\shape default
 выполнялись одновременно.
\end_layout

\begin_layout Section
Таймауты
\end_layout

\begin_layout Standard
Таймауты используются в программах, которые подключаются ко внешним ресурсам,
 или в программах, которые должны выполняться определённое время.
 Благодаря каналам и оператору 
\shape italic
select
\shape default
, реализация таймаутов в Go проста и элегантна.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "time"
\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Для нашего примера предположим, что мы выполняем внешний
\end_layout

\begin_layout Plain Layout

	//вызов, который возвращает результат в канал c1 после 2
\end_layout

\begin_layout Plain Layout

	//секунд
\end_layout

\begin_layout Plain Layout

	c1 := make(chan string, 1)
\end_layout

\begin_layout Plain Layout

	go func() {
\end_layout

\begin_layout Plain Layout

		time.Sleep(time.Second * 2)
\end_layout

\begin_layout Plain Layout

		c1 <- "result 1"
\end_layout

\begin_layout Plain Layout

    }()
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//В примере ниже представлена конструкция select, реализующая
\end_layout

\begin_layout Plain Layout

	//таймаут.
 res := <-c1 ожидает результата, а <-Time.After ожидает,
\end_layout

\begin_layout Plain Layout

	//когда сообщение будет отправлено после таймаута в одун секунду.
\end_layout

\begin_layout Plain Layout

	//Поскольку select будет продолжаться после первого успешного
\end_layout

\begin_layout Plain Layout

	//приёма сообщения, то, если операция будет продолжаться дольше
\end_layout

\begin_layout Plain Layout

	//одной секунды, будет выбран вариант с таймаутом
\end_layout

\begin_layout Plain Layout

	select {
\end_layout

\begin_layout Plain Layout

	case res := <-c1:
\end_layout

\begin_layout Plain Layout

		fmt.Println(res)
\end_layout

\begin_layout Plain Layout

	case <-time.After(time.Second * 1):
\end_layout

\begin_layout Plain Layout

		fmt.Println("timeout 1")
\end_layout

\begin_layout Plain Layout

    }
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Если мы зададим таймаут в 3 секунды, то мы успеем получить
\end_layout

\begin_layout Plain Layout

	//сообщение от c2 и на печать будет выведен результат
\end_layout

\begin_layout Plain Layout

	c2 := make(chan string, 1)
\end_layout

\begin_layout Plain Layout

	go func() {
\end_layout

\begin_layout Plain Layout

		time.Sleep(time.Second * 2)
\end_layout

\begin_layout Plain Layout

		c2 <- "result 2"
\end_layout

\begin_layout Plain Layout

	}()
\end_layout

\begin_layout Plain Layout

	select {
\end_layout

\begin_layout Plain Layout

	case res := <-c2:
\end_layout

\begin_layout Plain Layout

		fmt.Println(res)
\end_layout

\begin_layout Plain Layout

	case <-time.After(time.Second * 3):
\end_layout

\begin_layout Plain Layout

		fmt.Println("timeout 2")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run timeouts.go 
\end_layout

\begin_layout LyX-Code
timeout 1
\end_layout

\begin_layout LyX-Code
result 2
\end_layout

\begin_layout Standard
Выполняя эту программу, мы увидим, что время первой операции истекло (это
 и был таймаут), тогда как вторая успешно выполнилась.
\end_layout

\begin_layout Standard
Многие важные возможности Go (как и паттерн select-таймаут, использование
 которого требует обмен результатами через каналы) основываются на использовании
 каналов и оператора 
\shape italic
select
\shape default
, следующие из них — таймеры и тикеры — мы рассмотрим позднее.
\end_layout

\begin_layout Section
Неблокирующие операции с каналами
\end_layout

\begin_layout Standard
Обычно операции приёма или отправки сообщений в каналах блокируют эти каналы.
 Но с помощью оператора 
\shape italic
select
\shape default
 с блоком 
\shape italic
default
\shape default
 мы можем реализовать неблокирующую отправку, приём сообщений и даже неблокирующ
ий многовариантный выбор (
\shape italic
select
\shape default
).
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	messages := make(chan string)
\end_layout

\begin_layout Plain Layout

	signals := make(chan bool)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//В этом примере показан неблокирующий приём.
 Если сообщение
\end_layout

\begin_layout Plain Layout

	//в канале messages доступно, то с этим сообщение
\end_layout

\begin_layout Plain Layout

	//будет исполнен первый блок кода (<-messages), тогда как
\end_layout

\begin_layout Plain Layout

	//в обратном случае будет выбран блок default
\end_layout

\begin_layout Plain Layout

	select {
\end_layout

\begin_layout Plain Layout

	case msg := <-messages:
\end_layout

\begin_layout Plain Layout

		fmt.Println("received message", msg)
\end_layout

\begin_layout Plain Layout

	default:
\end_layout

\begin_layout Plain Layout

		fmt.Println("no message received")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Неблокирующая отправка сообщений работает так же
\end_layout

\begin_layout Plain Layout

	msg := "hi"
\end_layout

\begin_layout Plain Layout

	select {
\end_layout

\begin_layout Plain Layout

	case messages <- msg:
\end_layout

\begin_layout Plain Layout

		fmt.Println("sent message", msg)
\end_layout

\begin_layout Plain Layout

	default:
\end_layout

\begin_layout Plain Layout

		fmt.Println("no message sent")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Мы можем поместить несколько блоков case перед
\end_layout

\begin_layout Plain Layout

	//блоком default, чтобы реализовать многовариантный
\end_layout

\begin_layout Plain Layout

	//неблокирующий select.
 В этом примере мы пытаемся
\end_layout

\begin_layout Plain Layout

	//произвести неблокирующий приём сообщений
\end_layout

\begin_layout Plain Layout

	//одновременно в messages и signals
\end_layout

\begin_layout Plain Layout

	select {
\end_layout

\begin_layout Plain Layout

	case msg := <-messages:
\end_layout

\begin_layout Plain Layout

		fmt.Println("received message", msg)
\end_layout

\begin_layout Plain Layout

	case sig := <-signals:
\end_layout

\begin_layout Plain Layout

		fmt.Println("received signal", sig)
\end_layout

\begin_layout Plain Layout

	default:
\end_layout

\begin_layout Plain Layout

		fmt.Println("no activity")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run non-blocking-channel-operations.go 
\end_layout

\begin_layout LyX-Code
no message received
\end_layout

\begin_layout LyX-Code
no message sent
\end_layout

\begin_layout LyX-Code
no activity
\end_layout

\begin_layout Section
Закрытие каналов
\end_layout

\begin_layout Standard
Закрытие канала означает, что в него больше нельзя будет отправить сообщение.
 Эта возможность может быть полезна в том случае, когад требуется сообщить
 принимающим горутинам о завершении приёма.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//В этом примере мы используем канал jobs для сообщения
\end_layout

\begin_layout Plain Layout

	//о выполнении работы.
 Сообщение будет отправлено из
\end_layout

\begin_layout Plain Layout

	//горутины main() в горутину, выполняющую работу.
 Когда
\end_layout

\begin_layout Plain Layout

	//вся работа будет выполнена, мы закроем канал jobs
\end_layout

\begin_layout Plain Layout

	jobs := make(chan int, 5)
\end_layout

\begin_layout Plain Layout

	done := make(chan bool)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Это горутина, выполняющая работу.
 Она получает сообщения
\end_layout

\begin_layout Plain Layout

	//из jobs (j, more := <-jobs).
 В этот раз мы принимаем
\end_layout

\begin_layout Plain Layout

	//два значения: значение more бдует ложным (false), если
\end_layout

\begin_layout Plain Layout

	//канал jobs будет закрыт и все задания будут
\end_layout

\begin_layout Plain Layout

	//приняты.
 Мы используем эту возможность для того, чтобы
\end_layout

\begin_layout Plain Layout

	//отправить сообщение в канал done в том случае, когда
\end_layout

\begin_layout Plain Layout

	//вся работа будет выполнена
\end_layout

\begin_layout Plain Layout

	go func() {
\end_layout

\begin_layout Plain Layout

		for {
\end_layout

\begin_layout Plain Layout

			j, more := <-jobs
\end_layout

\begin_layout Plain Layout

			if more {
\end_layout

\begin_layout Plain Layout

				fmt.Println("received job", j)
\end_layout

\begin_layout Plain Layout

			} else {
\end_layout

\begin_layout Plain Layout

				fmt.Println("received all jobs")
\end_layout

\begin_layout Plain Layout

				done <- true
\end_layout

\begin_layout Plain Layout

				return
\end_layout

\begin_layout Plain Layout

			}
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}()
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Здесь мы посылаем три задания в канал jobs,
\end_layout

\begin_layout Plain Layout

	//а затем закрываем его
\end_layout

\begin_layout Plain Layout

	for j := 1; j <= 3; j++ {
\end_layout

\begin_layout Plain Layout

		jobs <- j
\end_layout

\begin_layout Plain Layout

		fmt.Println("sent job", j)
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	close(jobs)
\end_layout

\begin_layout Plain Layout

	fmt.Println("sent all jobs")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Чтобы программа не завершилась досрочно, мы используем
\end_layout

\begin_layout Plain Layout

	//синхронизацию, которая была описана в прошлых разделах
\end_layout

\begin_layout Plain Layout

	<-done
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run closing-channels.go 
\end_layout

\begin_layout LyX-Code
sent job 1
\end_layout

\begin_layout LyX-Code
received job 1
\end_layout

\begin_layout LyX-Code
sent job 2
\end_layout

\begin_layout LyX-Code
received job 2
\end_layout

\begin_layout LyX-Code
sent job 3
\end_layout

\begin_layout LyX-Code
received job 3
\end_layout

\begin_layout LyX-Code
sent all jobs
\end_layout

\begin_layout LyX-Code
received all jobs
\end_layout

\begin_layout Standard
Закрытие каналов мы будем использовать и в следующем разделе, посвящённом
 перебору значений в каналах.
\end_layout

\begin_layout Section
Перебор значений в каналах
\end_layout

\begin_layout Standard
В предыдущих примерах мы видели, как с помощью 
\shape italic
for
\shape default
 и 
\shape italic
range 
\shape default
мы можем обойти (iterate) все значения в основных структурах данных, но
 мы так же можем использовать эти операторы для обхода значений в каналах.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Мы обходим 2 значения в канале queue
\end_layout

\begin_layout Plain Layout

	queue := make(chan string, 2)
\end_layout

\begin_layout Plain Layout

	queue <- "one"
\end_layout

\begin_layout Plain Layout

	queue <- "two"
\end_layout

\begin_layout Plain Layout

	close(queue)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//range обходит все элементы в том порядке, в каком
\end_layout

\begin_layout Plain Layout

	//они были приняты в queue.
 После обхода двух элементов
\end_layout

\begin_layout Plain Layout

	//итерация прерывается, т.к.
 мы закрыли канал queue выше.
\end_layout

\begin_layout Plain Layout

	//Если бы мы не сделали этого, то мы бы остановились на
\end_layout

\begin_layout Plain Layout

	//третьем элементе
\end_layout

\begin_layout Plain Layout

	for elem := range queue {
\end_layout

\begin_layout Plain Layout

		fmt.Println(elem)
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run range-over-channels.go
\end_layout

\begin_layout LyX-Code
one
\end_layout

\begin_layout LyX-Code
two
\end_layout

\begin_layout Standard
Этот пример также показывает, что мы можем закрыть непустой канал, при этом
 сохранив сообщения в нём.
\end_layout

\begin_layout Section
Таймеры
\end_layout

\begin_layout Standard
Зачастую нужно выполнить какой-то код в какое-то время в будущем, или выполнять
 его многократно через определённый интервал.
 В Go для этих целей служат таймеры (которые мы разберём в первую очередь)
 и тикеры (рассмотрим их в следующем разделе).
 
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "time"
\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Таймеры представляют какое-то определённое событие в
\end_layout

\begin_layout Plain Layout

	//будущем.
 Таймеру нужно указать, сколько времени ему
\end_layout

\begin_layout Plain Layout

	//нужно ждать, и он создаст канал, в который будет
\end_layout

\begin_layout Plain Layout

	//отправлено сообщение по истечении срока.
 Таймер в
\end_layout

\begin_layout Plain Layout

	//этмо примере будет ждать две секунды
\end_layout

\begin_layout Plain Layout

	timer1 := time.NewTimer(time.Second * 2)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//<-timer1.C блокирует канал до тех пор, пока не будет
\end_layout

\begin_layout Plain Layout

	//послано сообщение о том, что время истекло
\end_layout

\begin_layout Plain Layout

	<-timer1.C
\end_layout

\begin_layout Plain Layout

	fmt.Println("Timer 1 expired")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Как показано на примере ниже, таймеры бывают полезны,
\end_layout

\begin_layout Plain Layout

	//например, тем, что их можно отменить до истечения
\end_layout

\begin_layout Plain Layout

	//времени (если же нужно просто выждать какое-то время,
\end_layout

\begin_layout Plain Layout

	//то можно использовать time.Sleep)
\end_layout

\begin_layout Plain Layout

	timer2 := time.NewTimer(time.Second)
\end_layout

\begin_layout Plain Layout

	go func() {
\end_layout

\begin_layout Plain Layout

		<-timer2.C
\end_layout

\begin_layout Plain Layout

		fmt.Println("Timer 2 expired")
\end_layout

\begin_layout Plain Layout

	}()
\end_layout

\begin_layout Plain Layout

	stop2 := timer2.Stop()
\end_layout

\begin_layout Plain Layout

	if stop2 {
\end_layout

\begin_layout Plain Layout

		fmt.Println("Timer 2 stopped")
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run timers.go
\end_layout

\begin_layout LyX-Code
Timer 1 expired
\end_layout

\begin_layout LyX-Code
Timer 2 stopped
\end_layout

\begin_layout Standard
Время первого таймера истекло спустя 2 секунды после запуска программы,
 но второй был остановлен ещё до того, как успел истечь.
\end_layout

\begin_layout Section
Тикеры
\end_layout

\begin_layout Standard
Таймеры предназначены для того, чтобы выполнить какое-то действие в будущем
 один раз.
 Тикеры же, в свою очередь, нужны, чтобы выполнять какое-то действие многократно
 через определённый интервал.
 В этом разделе мы увидим тикер, который 
\begin_inset Quotes eld
\end_inset

тикает
\begin_inset Quotes erd
\end_inset

 (по факту в нашем примере — печатает сообщение) до тех пор, пока не будет
 остановлен.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "time"
\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Тикеры работают примерно схожим образом с таймерами.
 Так
\end_layout

\begin_layout Plain Layout

	//же как и в случае с последними, в созданный тикером канал
\end_layout

\begin_layout Plain Layout

	//отправляются сообщения.
 В этом примере мы используем
\end_layout

\begin_layout Plain Layout

	//range, чтобы обойти все сообщения по мере их прибытия
\end_layout

\begin_layout Plain Layout

	//(в нашем примере -- каждые 500 миллисекунд)
\end_layout

\begin_layout Plain Layout

	ticker := time.NewTicker(time.Millisecond * 500)
\end_layout

\begin_layout Plain Layout

	go func() {
\end_layout

\begin_layout Plain Layout

		for t := range ticker.C {
\end_layout

\begin_layout Plain Layout

			fmt.Println("Tick at", t)
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}()
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Тикеры, как и таймеры, могут быть остановлены.
 После того
\end_layout

\begin_layout Plain Layout

	//как это будет сделано, в канале тикера больше не будут
\end_layout

\begin_layout Plain Layout

	//приниматься сообщения.
 В нашем примере мы остановим
\end_layout

\begin_layout Plain Layout

	//тикер по истечении 1600 миллисекунд
\end_layout

\begin_layout Plain Layout

	time.Sleep(time.Millisecond * 1600)
\end_layout

\begin_layout Plain Layout

	ticker.Stop()
\end_layout

\begin_layout Plain Layout

	fmt.Println("Ticker stopped")
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run tickers.go
\end_layout

\begin_layout LyX-Code
Tick at 2012-09-23 11:29:56.487625 -0700 PDT
\end_layout

\begin_layout LyX-Code
Tick at 2012-09-23 11:29:56.988063 -0700 PDT
\end_layout

\begin_layout LyX-Code
Tick at 2012-09-23 11:29:57.488076 -0700 PDT
\end_layout

\begin_layout LyX-Code
Ticker stopped
\end_layout

\begin_layout Standard
Во время выполнения этой программы тикер должен сработать 3 раза, перед
 тем как мы его остановим.
\end_layout

\begin_layout Section
Наборы обработчиков
\end_layout

\begin_layout Standard
В этом примере мы увидим, как реализовать набор обработчиков (worker pool),
 используя горутины и каналы.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

import "time"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//В функции ниже представлен наш обработчик (worker).
 Мы запустим
\end_layout

\begin_layout Plain Layout

//одновременно несколько параллельно работающих обработчиков, которые
\end_layout

\begin_layout Plain Layout

//будут получать задания из канала jobs и отправлять соответствующие
\end_layout

\begin_layout Plain Layout

//результаты в results.
 Чтобы симулировать работу, мы будем использовать
\end_layout

\begin_layout Plain Layout

//time.Sleep(time.Second)
\end_layout

\begin_layout Plain Layout

func worker(id int, jobs <-chan int, results chan<- int) {
\end_layout

\begin_layout Plain Layout

	for j := range jobs {
\end_layout

\begin_layout Plain Layout

		fmt.Println("worker", id, "processing job", j)
\end_layout

\begin_layout Plain Layout

		time.Sleep(time.Second)
\end_layout

\begin_layout Plain Layout

		results <- j * 2
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Чтобы использовать наш набор обработчиков, необходимо отправлять
\end_layout

\begin_layout Plain Layout

	//им задания и собирать результаты.
 Для этого мы создадим 2 канала
\end_layout

\begin_layout Plain Layout

	jobs := make(chan int, 100)
\end_layout

\begin_layout Plain Layout

	results := make(chan int, 100)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Этот блок кода запускает 3 обработчика, которые изначально
\end_layout

\begin_layout Plain Layout

	//блокированы, т.к.
 мы ещё не послали им задания
\end_layout

\begin_layout Plain Layout

	for w := 1; w <= 3; w++ {
\end_layout

\begin_layout Plain Layout

		go worker(w, jobs, results)
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Здесь мы отправляем 9 заданий и затем закрываем канал,
\end_layout

\begin_layout Plain Layout

	//чтобы сообщить о том, что больше заданий нет
\end_layout

\begin_layout Plain Layout

	for j := 1; j <= 9; j++ {
\end_layout

\begin_layout Plain Layout

		jobs <- j
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	close(jobs)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//И в конце концов мы собираем все результаты
\end_layout

\begin_layout Plain Layout

	for a := 1; a <= 9; a++ {
\end_layout

\begin_layout Plain Layout

		<-results
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ time go run worker-pools.go 
\end_layout

\begin_layout LyX-Code
worker 1 processing job 1
\end_layout

\begin_layout LyX-Code
worker 2 processing job 2
\end_layout

\begin_layout LyX-Code
worker 3 processing job 3
\end_layout

\begin_layout LyX-Code
worker 1 processing job 4
\end_layout

\begin_layout LyX-Code
worker 2 processing job 5
\end_layout

\begin_layout LyX-Code
worker 3 processing job 6
\end_layout

\begin_layout LyX-Code
worker 1 processing job 7
\end_layout

\begin_layout LyX-Code
worker 2 processing job 8
\end_layout

\begin_layout LyX-Code
worker 3 processing job 9
\end_layout

\begin_layout LyX-Code
real 0m3.149s
\end_layout

\begin_layout Standard
Во время выполнения программы мы видим, как 9 заданий выполняются разными
 обработчиками.
 Несмотря на то, что суммарно обработчики выполнили 9 секунд работы, на
 выполнение программы ушло всего около 3 секунд, т.к.
 3 обработчика работали одновременно.
\end_layout

\begin_layout Section
Ограничение скорости
\end_layout

\begin_layout Standard
Ограничение скорости — это важный механизм, который используется для контроля
 использования ресурсов.
 Go элегантно поддерживает ограничение скорости с помощью горутин, каналов
 и тикеров.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "time"
\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Для начала мы рассмотрим базовое ограничение скорости.
\end_layout

\begin_layout Plain Layout

	//Допустим, мы хотим ограничить объём обрабатываемых
\end_layout

\begin_layout Plain Layout

	//входящих запросов.
 Мы будем работать с ними через канал
\end_layout

\begin_layout Plain Layout

	//requests
\end_layout

\begin_layout Plain Layout

	requests := make(chan int, 5)
\end_layout

\begin_layout Plain Layout

	for i := 1; i <= 5; i++ {
\end_layout

\begin_layout Plain Layout

		requests <- i
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	close(requests)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Канал limiter будет принимать сообщение каждые 200
\end_layout

\begin_layout Plain Layout

	//миллисекунд.
 В нашем случае он будет выступать регулятором
\end_layout

\begin_layout Plain Layout

	limiter := time.Tick(time.Millisecond * 200)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Блокируя получение сообщения перед обслуживанием каждого
\end_layout

\begin_layout Plain Layout

	//запроса, мы ограничиваем себя до 1 запроса каждые 200 мс
\end_layout

\begin_layout Plain Layout

	for req := range requests {
\end_layout

\begin_layout Plain Layout

		<-limiter
\end_layout

\begin_layout Plain Layout

		fmt.Println("request", req, time.Now())
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Однако, мы можем хотеть, чтобы на короткие периоды времени
\end_layout

\begin_layout Plain Layout

	//количество обрабатываемых запросов могло возрастать, но
\end_layout

\begin_layout Plain Layout

	//чтобы при этом в целом ограничения сохранялись.
 Мы можем
\end_layout

\begin_layout Plain Layout

	//добиться этого, буферизовав ограничивающий (limiter) канал.
\end_layout

\begin_layout Plain Layout

	//В данном примере мы видим канал burstyLimiter, который
\end_layout

\begin_layout Plain Layout

	//позволит нам увеличить скорость приёма для 3 сообщений
\end_layout

\begin_layout Plain Layout

	burstyLimiter := make(chan time.Time, 3)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Заполним канал, чтобы указать допустимое увеличение
\end_layout

\begin_layout Plain Layout

	//скорости
\end_layout

\begin_layout Plain Layout

	for i := 0; i < 3; i++ {
\end_layout

\begin_layout Plain Layout

		burstyLimiter <- time.Now()
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Каждые 200 миллисекунд мы пытается отправить новое
\end_layout

\begin_layout Plain Layout

	//сообщение в burstyLimiter, вплоть до ограничения в
\end_layout

\begin_layout Plain Layout

	//3 сообщения
\end_layout

\begin_layout Plain Layout

	go func() {
\end_layout

\begin_layout Plain Layout

		for t := range time.Tick(time.Millisecond * 200) {
\end_layout

\begin_layout Plain Layout

			burstyLimiter <- t
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}()
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Теперь же мы имитируем ещё 5 входящих запросов.
 Первые
\end_layout

\begin_layout Plain Layout

	//3 из них получат преимущество благодая возможности
\end_layout

\begin_layout Plain Layout

	//увеличения скорости, которую мы реализовали для
\end_layout

\begin_layout Plain Layout

	//burstyLimiter
\end_layout

\begin_layout Plain Layout

	burstyRequests := make(chan int, 5)
\end_layout

\begin_layout Plain Layout

	for i := 1; i <= 5; i++ {
\end_layout

\begin_layout Plain Layout

		burstyRequests <- i
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	close(burstyRequests)
\end_layout

\begin_layout Plain Layout

	for req := range burstyRequests {
\end_layout

\begin_layout Plain Layout

		<-burstyLimiter
\end_layout

\begin_layout Plain Layout

		fmt.Println("request", req, time.Now())
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run rate-limiting.go
\end_layout

\begin_layout LyX-Code
request 1 2012-10-19 00:38:18.687438 +0000 UTC
\end_layout

\begin_layout LyX-Code
request 2 2012-10-19 00:38:18.887471 +0000 UTC
\end_layout

\begin_layout LyX-Code
request 3 2012-10-19 00:38:19.087238 +0000 UTC
\end_layout

\begin_layout LyX-Code
request 4 2012-10-19 00:38:19.287338 +0000 UTC
\end_layout

\begin_layout LyX-Code
request 5 2012-10-19 00:38:19.487331 +0000 UTC
\end_layout

\begin_layout LyX-Code
request 1 2012-10-19 00:38:20.487578 +0000 UTC
\end_layout

\begin_layout LyX-Code
request 2 2012-10-19 00:38:20.487645 +0000 UTC
\end_layout

\begin_layout LyX-Code
request 3 2012-10-19 00:38:20.487676 +0000 UTC
\end_layout

\begin_layout LyX-Code
request 4 2012-10-19 00:38:20.687483 +0000 UTC
\end_layout

\begin_layout LyX-Code
request 5 2012-10-19 00:38:20.887542 +0000 UTC
\end_layout

\begin_layout Standard
Выполняя нашу программу, мы видим, что первая группа запросов обслуживается
 по одному каждые ~200 миллисекунд, как и планировалось.
\end_layout

\begin_layout Standard
Но во второй группе первые 3 запроса были обслужены немедленно, благодаря
 возможности временного увеличения скорости.
 Оставшиеся же 2 запрсоа были обработаны как обычно, с задержкой в ~200
 мс.
\end_layout

\begin_layout Section
Атомарные счётчики
\end_layout

\begin_layout Standard
В основном для управления состоянием в Go используются каналы и передача
 сообщений через них (пример мы видели в разделе про наборы обработчиков).
 Но помимо этого есть и ещё несколько механизмов.
 В этом разделе мы рассмотрим пакет 
\shape italic
sync/atomic
\shape default
, который реализует так называемые атомарные счётчики, которые доступны
 сразу нескольким горутинам.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

import "time"
\end_layout

\begin_layout Plain Layout

import "sync/atomic"
\end_layout

\begin_layout Plain Layout

import "runtime"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Для представления счётчика мы будем использовать
\end_layout

\begin_layout Plain Layout

	//беззнаковое (всегда позитивное) целое
\end_layout

\begin_layout Plain Layout

	var ops uint64 = 0
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Чтобы симулировать одновременные обновления, мы
\end_layout

\begin_layout Plain Layout

	//запустим 50 горутин, каждая из которых увеличивает
\end_layout

\begin_layout Plain Layout

	//счётчик примерно раз в миллисекунду
\end_layout

\begin_layout Plain Layout

	for i := 0; i < 50; i++ {
\end_layout

\begin_layout Plain Layout

		go func() {
\end_layout

\begin_layout Plain Layout

			for {
\end_layout

\begin_layout Plain Layout

				//Чтобы атомарно обновлять счётчик,
\end_layout

\begin_layout Plain Layout

				//используем AddUint64, в качестве
\end_layout

\begin_layout Plain Layout

				//аргумента передавая адрес в памяти
\end_layout

\begin_layout Plain Layout

				//для нашего счётчика ops с помощью &
\end_layout

\begin_layout Plain Layout

				atomic.AddUint64(&ops, 1)
\end_layout

\begin_layout Plain Layout

				//Позволяем другим горутинам продолжить
\end_layout

\begin_layout Plain Layout

				//выполнение
\end_layout

\begin_layout Plain Layout

				runtime.Gosched()
\end_layout

\begin_layout Plain Layout

			}
\end_layout

\begin_layout Plain Layout

		}()
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	//Подождём секунду, чтобы накопить результаты работы в ops
\end_layout

\begin_layout Plain Layout

	time.Sleep(time.Second)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Чтобы безопасно использовать счётчик, в то время, как он
\end_layout

\begin_layout Plain Layout

	//обновляется горутинами, извлекаем копию текущего значения
\end_layout

\begin_layout Plain Layout

	//в переменную opsFinal с помощью LoadUint64.
 Как и в примере
\end_layout

\begin_layout Plain Layout

	//выше, мы должны передать в качестве аргумента адрес &ops,
\end_layout

\begin_layout Plain Layout

	//чтобы получить значение
\end_layout

\begin_layout Plain Layout

	opsFinal := atomic.LoadUint64(&ops)
\end_layout

\begin_layout Plain Layout

	fmt.Println("ops:", opsFinal)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run atomic-counters.go
\end_layout

\begin_layout LyX-Code
ops: 40200
\end_layout

\begin_layout Standard
Запустив эту программу, мы увидим, что было выполнено примерно 40 тысяч
 операций.
\end_layout

\begin_layout Standard
В следующем разделе мы рассмотрим мьютексы, ещё один инструмент для управления
 состоянием.
\end_layout

\begin_layout Section
Мьютексы
\end_layout

\begin_layout Standard
В предыдущем разделе мы видели, как можно управлять простым счётчиком с
 помощью атомарных операций.
 Для более комплексного управления состоянием же мы можем использовать мьютексы
 (
\begin_inset CommandInset href
LatexCommand href
target "https://ru.wikipedia.org/wiki/Мьютекс"

\end_inset

), которые позволяют обеспечить безопасный доступ к данным более чем одной
 горутинам.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import (
\end_layout

\begin_layout Plain Layout

    "fmt"
\end_layout

\begin_layout Plain Layout

    "math/rand"
\end_layout

\begin_layout Plain Layout

    "runtime"
\end_layout

\begin_layout Plain Layout

    "sync"
\end_layout

\begin_layout Plain Layout

    "sync/atomic"
\end_layout

\begin_layout Plain Layout

    "time"
\end_layout

\begin_layout Plain Layout

)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//В нашем примере переменная, отвечающая за состояние (state),
\end_layout

\begin_layout Plain Layout

	//будет хешом
\end_layout

\begin_layout Plain Layout

	var state = make(map[int]int)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//mutex будет синхронизировать доступ к state
\end_layout

\begin_layout Plain Layout

	var mutex = &sync.Mutex{}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Чтобы сравнить мьютексы с другими инструментами управления
\end_layout

\begin_layout Plain Layout

	//состоянием, которые мы рассмотрим позже, ops будет считать
\end_layout

\begin_layout Plain Layout

	//количество операций над состоянием
\end_layout

\begin_layout Plain Layout

	var ops int64 = 0
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Запускаем 100 горутин, которые будут выполнять чтение
\end_layout

\begin_layout Plain Layout

	//состояния снова и снова
\end_layout

\begin_layout Plain Layout

	for r := 0; r < 100; r++ {
\end_layout

\begin_layout Plain Layout

		go func() {
\end_layout

\begin_layout Plain Layout

			total := 0
\end_layout

\begin_layout Plain Layout

			for {
\end_layout

\begin_layout Plain Layout

				//Каждый раз при чтении сначала мы выбираем ключ,
\end_layout

\begin_layout Plain Layout

				//блокируем (Lock()) мьютекс, чтобы убедиться,
\end_layout

\begin_layout Plain Layout

				//что только эта горутина имеет доступ
\end_layout

\begin_layout Plain Layout

				//к состоянию, читаем значение с выбранным
\end_layout

\begin_layout Plain Layout

				//ключом, разблокируем (Unlock())
\end_layout

\begin_layout Plain Layout

				//мьютекс и увеличиваем счётчик ops
\end_layout

\begin_layout Plain Layout

				key := rand.Intn(5)
\end_layout

\begin_layout Plain Layout

				mutex.Lock()
\end_layout

\begin_layout Plain Layout

				total += state[key]
\end_layout

\begin_layout Plain Layout

				mutex.Unlock()
\end_layout

\begin_layout Plain Layout

				atomic.AddInt64(&ops, 1)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

				//Для того, чтобы убедиться в том, что горутина
\end_layout

\begin_layout Plain Layout

				//не остановит планировщик, явно выполняем
\end_layout

\begin_layout Plain Layout

				//runtime.Gosched() после каждой операции.
\end_layout

\begin_layout Plain Layout

				//runtime.Gosched() выполняется автоматически
\end_layout

\begin_layout Plain Layout

				//после операций с каналами или блокирующих
\end_layout

\begin_layout Plain Layout

				//вызовов (таких, как time.Sleep), но в данном
\end_layout

\begin_layout Plain Layout

				//случае нам необходимо сделать это вручную
\end_layout

\begin_layout Plain Layout

				runtime.Gosched()
\end_layout

\begin_layout Plain Layout

            }
\end_layout

\begin_layout Plain Layout

        }()
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Мы также запустим 10 горутин, которые будут симулировать
\end_layout

\begin_layout Plain Layout

	//запись, используя тот же подход, который мы применили
\end_layout

\begin_layout Plain Layout

	//для симуляции чтения
\end_layout

\begin_layout Plain Layout

	for w := 0; w < 10; w++ {
\end_layout

\begin_layout Plain Layout

		go func() {
\end_layout

\begin_layout Plain Layout

			for {
\end_layout

\begin_layout Plain Layout

				key := rand.Intn(5)
\end_layout

\begin_layout Plain Layout

				val := rand.Intn(100)
\end_layout

\begin_layout Plain Layout

				mutex.Lock()
\end_layout

\begin_layout Plain Layout

				state[key] = val
\end_layout

\begin_layout Plain Layout

				mutex.Unlock()
\end_layout

\begin_layout Plain Layout

				atomic.AddInt64(&ops, 1)
\end_layout

\begin_layout Plain Layout

				runtime.Gosched()
\end_layout

\begin_layout Plain Layout

			}
\end_layout

\begin_layout Plain Layout

		}()
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Позволим 10 горутинам работать над state и mutex в
\end_layout

\begin_layout Plain Layout

	//течение 1 секунды
\end_layout

\begin_layout Plain Layout

	time.Sleep(time.Second)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Окончательное число операций
\end_layout

\begin_layout Plain Layout

	opsFinal := atomic.LoadInt64(&ops)
\end_layout

\begin_layout Plain Layout

	fmt.Println("ops:", opsFinal)
\end_layout

\begin_layout Plain Layout

	
\end_layout

\begin_layout Plain Layout

	//Блокируем (фиксируем) окончательное состояние
\end_layout

\begin_layout Plain Layout

	//state и выводим его на экран
\end_layout

\begin_layout Plain Layout

	mutex.Lock()
\end_layout

\begin_layout Plain Layout

	fmt.Println("state:", state)
\end_layout

\begin_layout Plain Layout

	mutex.Unlock()
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run mutexes.go
\end_layout

\begin_layout LyX-Code
ops: 3598302
\end_layout

\begin_layout LyX-Code
state: map[1:38 4:98 2:23 3:85 0:44]
\end_layout

\begin_layout Standard
Выполняя программу, видим, что мы выполнили примерно 3,5 миллиона операций
 над 
\shape italic
state
\shape default
, синхронизировав доступ к нему с помощью мьютексов.
\end_layout

\begin_layout Standard
В следующем разделе мы выполним эту же задачу с помощью одних только горутин
 и каналов.
\end_layout

\begin_layout Section
Горутины, сохраняющие состояние
\end_layout

\begin_layout Standard
В предыдущем разделе мы, для того, чтобы синхронизировать доступ к состоянию
 между горутинами, использовали блокирование с помощью мьютексов.
 Но это можно сделать и другими путями, например с помощью встроенных в
 Go возможностей по синхронизации горутин и каналов.
 Этот подход, основанный на использовании каналов, сочетается с распространённым
и в Go идеями, в частности, идее о том, что разделять память следует, используя
 обмен данными, и о том, что каждой единицей данных должна обладать только
 1 горутина.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import (
\end_layout

\begin_layout Plain Layout

	"fmt"
\end_layout

\begin_layout Plain Layout

	"math/rand"
\end_layout

\begin_layout Plain Layout

	"sync/atomic"
\end_layout

\begin_layout Plain Layout

	"time"
\end_layout

\begin_layout Plain Layout

)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//В нашем примере состояние будет принадлежать одной горутине.
\end_layout

\begin_layout Plain Layout

//Это даёт нам гарантию того, что данные не будут испорчены при
\end_layout

\begin_layout Plain Layout

//совместном доступе.
 Чтобы записать или считать состояние,
\end_layout

\begin_layout Plain Layout

//остальные горутины будут посылать этой горутине сообщения и
\end_layout

\begin_layout Plain Layout

//получать соответствующие ответы.
 Структуры readOp и writeOp
\end_layout

\begin_layout Plain Layout

//инкапсулируют эти запросы и то, как горутина будет отвечать
\end_layout

\begin_layout Plain Layout

//на них
\end_layout

\begin_layout Plain Layout

type readOp struct {
\end_layout

\begin_layout Plain Layout

	key  int
\end_layout

\begin_layout Plain Layout

	resp chan int
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

type writeOp struct {
\end_layout

\begin_layout Plain Layout

	key  int
\end_layout

\begin_layout Plain Layout

	val  int
\end_layout

\begin_layout Plain Layout

	resp chan bool
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Как и в предыдущих примерах, мы будем считать, сколько
\end_layout

\begin_layout Plain Layout

	//операций мы выполнили
\end_layout

\begin_layout Plain Layout

	var ops int64 = 0
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Каналы reads и writes будут использоваться другими
\end_layout

\begin_layout Plain Layout

	//горутинами для того, чтобы запросить чтение или запись
\end_layout

\begin_layout Plain Layout

	reads := make(chan *readOp)
\end_layout

\begin_layout Plain Layout

	writes := make(chan *writeOp)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Это горутина, владеющая состоянием.
 Состояние в данном
\end_layout

\begin_layout Plain Layout

	//случае -- это хеш, как и в прошлом примере.
 Но в этот раз
\end_layout

\begin_layout Plain Layout

	//хеш доступен только этой горутине.
 Эта горутина постоянно
\end_layout

\begin_layout Plain Layout

	//выполняет select над каналами reads и writes, отвечая на
\end_layout

\begin_layout Plain Layout

	//запросы по мере их прибытия.
 Запрос выполняется так:
\end_layout

\begin_layout Plain Layout

	//сначала выполняется запрошенная операция (чтение или запись),
\end_layout

\begin_layout Plain Layout

	//затем отправляется сообщение в канал resp, сообщающее о том,
\end_layout

\begin_layout Plain Layout

	//что операция прошла успешно (в случае с запросом на чтение
\end_layout

\begin_layout Plain Layout

	//отправляется также запрошенное значение)
\end_layout

\begin_layout Plain Layout

	go func() {
\end_layout

\begin_layout Plain Layout

		var state = make(map[int]int)
\end_layout

\begin_layout Plain Layout

		for {
\end_layout

\begin_layout Plain Layout

			select {
\end_layout

\begin_layout Plain Layout

			case read := <-reads:
\end_layout

\begin_layout Plain Layout

				read.resp <- state[read.key]
\end_layout

\begin_layout Plain Layout

			case write := <-writes:
\end_layout

\begin_layout Plain Layout

				state[write.key] = write.val
\end_layout

\begin_layout Plain Layout

				write.resp <- true
\end_layout

\begin_layout Plain Layout

			}
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}()
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Здесь мы запускаем 100 горутин, которые запрашивают чтение у
\end_layout

\begin_layout Plain Layout

	//горутины, владеющей состоянием, с помощью канала reads.
 Во
\end_layout

\begin_layout Plain Layout

	//время каждого запроса на чтение конструируется структура readOp,
\end_layout

\begin_layout Plain Layout

	//которая отправляется в канал reads.
 Затем принимается результат
\end_layout

\begin_layout Plain Layout

	//с помощью канала resp
\end_layout

\begin_layout Plain Layout

	for r := 0; r < 100; r++ {
\end_layout

\begin_layout Plain Layout

		go func() {
\end_layout

\begin_layout Plain Layout

			for {
\end_layout

\begin_layout Plain Layout

				read := &readOp{
\end_layout

\begin_layout Plain Layout

					key:  rand.Intn(5),
\end_layout

\begin_layout Plain Layout

					resp: make(chan int)}
\end_layout

\begin_layout Plain Layout

				reads <- read
\end_layout

\begin_layout Plain Layout

				<-read.resp
\end_layout

\begin_layout Plain Layout

				atomic.AddInt64(&ops, 1)
\end_layout

\begin_layout Plain Layout

			}
\end_layout

\begin_layout Plain Layout

		}()
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Тем же подходом мы запускаем 10 запросов на запись
\end_layout

\begin_layout Plain Layout

	for w := 0; w < 10; w++ {
\end_layout

\begin_layout Plain Layout

		go func() {
\end_layout

\begin_layout Plain Layout

			for {
\end_layout

\begin_layout Plain Layout

				write := &writeOp{
\end_layout

\begin_layout Plain Layout

					key:  rand.Intn(5),
\end_layout

\begin_layout Plain Layout

					val:  rand.Intn(100),
\end_layout

\begin_layout Plain Layout

					resp: make(chan bool)}
\end_layout

\begin_layout Plain Layout

				writes <- write
\end_layout

\begin_layout Plain Layout

				<-write.resp
\end_layout

\begin_layout Plain Layout

				atomic.AddInt64(&ops, 1)
\end_layout

\begin_layout Plain Layout

			}
\end_layout

\begin_layout Plain Layout

		}()
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Позволим горутинам поработать секунду
\end_layout

\begin_layout Plain Layout

	time.Sleep(time.Second)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Фиксируем и выводим на экран счётчик ops
\end_layout

\begin_layout Plain Layout

	opsFinal := atomic.LoadInt64(&ops)
\end_layout

\begin_layout Plain Layout

	fmt.Println("ops:", opsFinal)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run stateful-goroutines.go
\end_layout

\begin_layout LyX-Code
ops: 807434
\end_layout

\begin_layout Standard
Выполнив нашу программу, мы можем увидеть, что основанное на горутинах управлени
е состоянием позволило добиться выполнения около 800 тысяч операций в секунду.
\end_layout

\begin_layout Standard
В этом конкретном случае подход, основанный на горутинах, был чуть более
 сложным, чем использование мьютексов.
 Но в некоторых случаях он всё-таки может быть полезным, например, тогда,
 когда используются ещё и другие каналы, или тогда, когда организация такого
 большого количества мьютексов может ненароком привести к ошибке.
 Использовать тот или иной подход следует в зависимости от того, каккой
 из них будет более естественным в том или ином случае, главное — иметь
 возможность разобраться в конечном результате.
\end_layout

\begin_layout Section
Сортировка
\end_layout

\begin_layout Standard
В пакете 
\shape italic
sort
\shape default
 представлены методы для сортировки типов данных, как встроенных, так и
 определённых пользователем.
 В этом примере мы рассмотрим сортировку данных встроенных типов.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

import "sort"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Методы сортировки зависят от того, данные какого типа
\end_layout

\begin_layout Plain Layout

	//требуется сортировать.
 Ниже приведён пример для строк.
\end_layout

\begin_layout Plain Layout

	//Стоит отметить, что в результате сортировки модифицируется
\end_layout

\begin_layout Plain Layout

	//уже имеющийся срез, а не возвращается новый
\end_layout

\begin_layout Plain Layout

	strs := []string{"c", "a", "b"}
\end_layout

\begin_layout Plain Layout

	sort.Strings(strs)
\end_layout

\begin_layout Plain Layout

	fmt.Println("Strings:", strs)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Сортировка среза из целых чисел
\end_layout

\begin_layout Plain Layout

	ints := []int{7, 2, 4}
\end_layout

\begin_layout Plain Layout

	sort.Ints(ints)
\end_layout

\begin_layout Plain Layout

	fmt.Println("Ints:   ", ints)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//sort также можно использовать для того, чтобы узнать,
\end_layout

\begin_layout Plain Layout

	//отсортирован ли уже срез
\end_layout

\begin_layout Plain Layout

	s := sort.IntsAreSorted(ints)
\end_layout

\begin_layout Plain Layout

	fmt.Println("Sorted: ", s)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run sorting.go
\end_layout

\begin_layout LyX-Code
Strings: [a b c]
\end_layout

\begin_layout LyX-Code
Ints: [2 4 7]
\end_layout

\begin_layout LyX-Code
Sorted: true
\end_layout

\begin_layout Standard
Наша программа выводит на экран отсортированные срезы строк и целых чисел,
 а также положительный результат (
\shape italic
true
\shape default
) для нашего теста на то, был ли отсортирован срез.
\end_layout

\begin_layout Section
Произвольная сортировка
\end_layout

\begin_layout Standard
Иногда нам необходимо отсортировать срез не в обыкновенном порядке, а определённ
ым специфическим образом.
 Например, отсортировать строки по их длине, а не по алфавитному порядку.
 Произвольная сортировка — тема этого раздела.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "sort"
\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//В Go, для того, чтобы выполнить произвольную сортировку,
\end_layout

\begin_layout Plain Layout

//необходимо создать соответствующий тип.
 В строке ниже мы
\end_layout

\begin_layout Plain Layout

//создаём тип ByLength ("по длине"), который является алиасом
\end_layout

\begin_layout Plain Layout

//для встроенного типа []string
\end_layout

\begin_layout Plain Layout

type ByLength []string
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Создаём sort.Interface (интерфейс для сортировки) с методами
\end_layout

\begin_layout Plain Layout

//Len (сокращённо от "длина"), Less ("меньше") и Swap ("перемена
\end_layout

\begin_layout Plain Layout

//мест") для нашего типа, благодаря чему мы получаем возможность
\end_layout

\begin_layout Plain Layout

//использовать оригинальную функцию Sort из одноимённого пакета.
\end_layout

\begin_layout Plain Layout

//Методы Len и Swap обычно похожи вне зависимости от типа, тогда
\end_layout

\begin_layout Plain Layout

//как непосредственно сам принцип сортировки будет заложен в Less.
\end_layout

\begin_layout Plain Layout

//В нашем случае мы реализуем сортировку по длине (от меньшей
\end_layout

\begin_layout Plain Layout

//к большей), поэтому используем len(s[i]) и len(s[j])
\end_layout

\begin_layout Plain Layout

func (s ByLength) Len() int {
\end_layout

\begin_layout Plain Layout

	return len(s)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

func (s ByLength) Swap(i, j int) {
\end_layout

\begin_layout Plain Layout

	s[i], s[j] = s[j], s[i]
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

func (s ByLength) Less(i, j int) bool {
\end_layout

\begin_layout Plain Layout

	return len(s[i]) < len(s[j])
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Реализовав все необходимые методы, мы можем наконец использовать
\end_layout

\begin_layout Plain Layout

//нашу сортировку.
 Для этого мы приводим срез fruits к типу ByLength,
\end_layout

\begin_layout Plain Layout

//и затем вызываем sort.Sort
\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	fruits := []string{"peach", "banana", "kiwi"}
\end_layout

\begin_layout Plain Layout

	sort.Sort(ByLength(fruits))
\end_layout

\begin_layout Plain Layout

	fmt.Println(fruits)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run sorting-by-functions.go 
\end_layout

\begin_layout LyX-Code
[kiwi peach banana]
\end_layout

\begin_layout Standard
Выполнив нашу программу, мы видим, что была выполнена сортировка по длине,
 как мы и планировали.
\end_layout

\begin_layout Standard
Используя этот подход — создать произвольный тип, реализовать три метода
 интерфейса для него, и затем выполнить 
\shape italic
sort.Sort
\shape default
 с набором данных этого типа — мы можем выполнять любую другую произвольную
 сортировку в Go.
\end_layout

\begin_layout Section
Паника
\end_layout

\begin_layout Standard
Паника означает, что какая-либо операция была проведена неправильно.
 Паника обычно используется в тех случаях, когда необходимо завершить работу
 при ошибках, которые обычно не должны возникать, или тогда, когда мы не
 можем правильно обработать ошибку.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "os"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//В этом учебнике мы используем панику в случае непредвиденных
\end_layout

\begin_layout Plain Layout

	//ошибок.
 В этом примере же мы намерено используем панику, для
\end_layout

\begin_layout Plain Layout

	//того, чтобы показать, как с ней работать
\end_layout

\begin_layout Plain Layout

	panic("a problem")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Часто паника используется для того, чтобы прервать выполнение
\end_layout

\begin_layout Plain Layout

	//программы тогда, когда функция возвращает неизвестный код 
\end_layout

\begin_layout Plain Layout

	//ошибки, который мы не можем (или не желаем) обработать.
 В этом
\end_layout

\begin_layout Plain Layout

	//примере мы вызываем панику, когда получаем непредвиденную ошибку
\end_layout

\begin_layout Plain Layout

	//при создании файла
\end_layout

\begin_layout Plain Layout

	_, err := os.Create("/tmp/file")
\end_layout

\begin_layout Plain Layout

	if err != nil {
\end_layout

\begin_layout Plain Layout

		panic(err)
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run panic.go
\end_layout

\begin_layout LyX-Code
panic: a problem
\end_layout

\begin_layout LyX-Code

\end_layout

\begin_layout LyX-Code
goroutine 1 [running]:
\end_layout

\begin_layout LyX-Code
main.main()
\end_layout

\begin_layout LyX-Code
/.../panic.go:12 +0x47
\end_layout

\begin_layout LyX-Code

\end_layout

\begin_layout LyX-Code
...
\end_layout

\begin_layout LyX-Code
exit status 2
\end_layout

\begin_layout Standard
Выполнение этой программы приведёт её к панике, после чего будет выведено
 сообщение об ошибке и трейсы горутины, и в результате программа завершится
 с ненулевым статусом.
\end_layout

\begin_layout Standard
Следует отметить, что, в отличие от многих других языков, в которых используются
 исключения для обработки ошибок, в Go для этого предлагается возвращать
 значения, сообщающие об ошибке.
\end_layout

\begin_layout Section
Defer
\end_layout

\begin_layout Standard

\shape italic
defer
\shape default
 используется для того, чтобы отложить вызов указанной функции до тех пор,
 пока не завершится текущая функция — он находит применение, например, в
 случае необходимости высвободить ресурсы.
 Аналогами 
\shape italic
defer
\shape default
 в других языках являются 
\shape italic
ensure
\shape default
 и 
\shape italic
finally
\shape default
.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

import "os"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Допустим, требуется создать какой-либо файл, записать в него
\end_layout

\begin_layout Plain Layout

//информацию, и закрыть по завершении работы.
 Ниже показано, как
\end_layout

\begin_layout Plain Layout

//сделать это с использованием defer
\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//После создания файла с помощью createFile, откладываем
\end_layout

\begin_layout Plain Layout

	//вызов функции closeFile ("закрыть файл") с помощью defer.
\end_layout

\begin_layout Plain Layout

	//Эта функция выполнится в конце программы, после выполнения
\end_layout

\begin_layout Plain Layout

	//функции writeFile ("записать в файл")
\end_layout

\begin_layout Plain Layout

	f := createFile("/tmp/defer.txt")
\end_layout

\begin_layout Plain Layout

	defer closeFile(f)
\end_layout

\begin_layout Plain Layout

	writeFile(f)
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func createFile(p string) *os.File {
\end_layout

\begin_layout Plain Layout

	fmt.Println("creating")
\end_layout

\begin_layout Plain Layout

	f, err := os.Create(p)
\end_layout

\begin_layout Plain Layout

	if err != nil {
\end_layout

\begin_layout Plain Layout

		panic(err)
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	return f
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func writeFile(f *os.File) {
\end_layout

\begin_layout Plain Layout

	fmt.Println("writing")
\end_layout

\begin_layout Plain Layout

	fmt.Fprintln(f, "data")
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func closeFile(f *os.File) {
\end_layout

\begin_layout Plain Layout

	fmt.Println("closing")
\end_layout

\begin_layout Plain Layout

	f.Close()
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run defer.go
\end_layout

\begin_layout LyX-Code
creating
\end_layout

\begin_layout LyX-Code
writing
\end_layout

\begin_layout LyX-Code
closing
\end_layout

\begin_layout Standard
Выполнив эту программу, мы увидим, что закрытие файла действительно было
 выполнено уже после того, как была произведена запись.
\end_layout

\begin_layout Section
Функции для работы с коллекциями
\end_layout

\begin_layout Standard
Зачастую нам необходимо выполнять какие-либо действия над целыми группами
 данных — например, нам может понадобиться выбрать из группы все элементы,
 соответствующие какому-либо условию, или перераспределить элементы из одной
 группы в другую.
\end_layout

\begin_layout Standard
В некоторых языках для этого принято использовать технологии обобщённого
 программирования (
\begin_inset CommandInset href
LatexCommand href
target "https://ru.wikipedia.org/wiki/Обобщённое_программирование"

\end_inset

), так называемые дженерики.
 Но Go их не поддерживает, вместо них Go-программисты, когда возникает такая
 потребность, предпочитают использовать функции для работы с коллекциями.
\end_layout

\begin_layout Standard
Ниже приведены несколько примеров таких функций для срезов из строк.
 Вы можете использовать эти примеры для создания собственных.
 Стоит отметить, что в некоторых случаях легче просто напрямую встроить
 код, чем вызывать для этого вспомогательную функцию.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "strings"
\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Возвращает индекс первого совпадения строки t в срезе;
\end_layout

\begin_layout Plain Layout

//в случае, если строка в срезе не обнаружена,
\end_layout

\begin_layout Plain Layout

//возвращается -1
\end_layout

\begin_layout Plain Layout

func Index(vs []string, t string) int {
\end_layout

\begin_layout Plain Layout

	for i, v := range vs {
\end_layout

\begin_layout Plain Layout

		if v == t {
\end_layout

\begin_layout Plain Layout

			return i
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	return -1
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Возвращает true в том случае, если строка t присутствует
\end_layout

\begin_layout Plain Layout

//в срезе
\end_layout

\begin_layout Plain Layout

func Include(vs []string, t string) bool {
\end_layout

\begin_layout Plain Layout

	return Index(vs, t) >= 0
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Возвращает true, если какая-либо строка в срезе
\end_layout

\begin_layout Plain Layout

//соответствует условию f
\end_layout

\begin_layout Plain Layout

func Any(vs []string, f func(string) bool) bool {
\end_layout

\begin_layout Plain Layout

	for _, v := range vs {
\end_layout

\begin_layout Plain Layout

		if f(v) {
\end_layout

\begin_layout Plain Layout

			return true
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	return false
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Возвращает true, если все строки в срезе соответствуют
\end_layout

\begin_layout Plain Layout

//условию f
\end_layout

\begin_layout Plain Layout

func All(vs []string, f func(string) bool) bool {
\end_layout

\begin_layout Plain Layout

	for _, v := range vs {
\end_layout

\begin_layout Plain Layout

		if !f(v) {
\end_layout

\begin_layout Plain Layout

			return false
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	return true
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Возвращает новый срез, в котором будут содержаться все
\end_layout

\begin_layout Plain Layout

//строки, соответствующие условию f
\end_layout

\begin_layout Plain Layout

func Filter(vs []string, f func(string) bool) []string {
\end_layout

\begin_layout Plain Layout

	vsf := make([]string, 0)
\end_layout

\begin_layout Plain Layout

	for _, v := range vs {
\end_layout

\begin_layout Plain Layout

		if f(v) {
\end_layout

\begin_layout Plain Layout

			vsf = append(vsf, v)
\end_layout

\begin_layout Plain Layout

		}
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	return vsf
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Возвращает новый срез, в котором будут содержаться строки
\end_layout

\begin_layout Plain Layout

//из оригинального среза, обработанные функцией f
\end_layout

\begin_layout Plain Layout

func Map(vs []string, f func(string) string) []string {
\end_layout

\begin_layout Plain Layout

	vsm := make([]string, len(vs))
\end_layout

\begin_layout Plain Layout

	for i, v := range vs {
\end_layout

\begin_layout Plain Layout

		vsm[i] = f(v)
\end_layout

\begin_layout Plain Layout

	}
\end_layout

\begin_layout Plain Layout

	return vsm
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Ниже мы пробуем наши функции для работы с коллекциями
\end_layout

\begin_layout Plain Layout

	var strs = []string{"peach", "apple", "pear", "plum"}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	fmt.Println(Index(strs, "pear"))
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	fmt.Println(Include(strs, "grape"))
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	fmt.Println(Any(strs, func(v string) bool {
\end_layout

\begin_layout Plain Layout

		return strings.HasPrefix(v, "p")
\end_layout

\begin_layout Plain Layout

	}))
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	fmt.Println(All(strs, func(v string) bool {
\end_layout

\begin_layout Plain Layout

		return strings.HasPrefix(v, "p")
\end_layout

\begin_layout Plain Layout

	}))
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	fmt.Println(Filter(strs, func(v string) bool {
\end_layout

\begin_layout Plain Layout

		return strings.Contains(v, "e")
\end_layout

\begin_layout Plain Layout

	}))
\end_layout

\begin_layout Plain Layout

	
\end_layout

\begin_layout Plain Layout

	//В примерах выше применялись анонимные функции,
\end_layout

\begin_layout Plain Layout

	//но мы можем использовать и именованные функции
\end_layout

\begin_layout Plain Layout

	//корректного типа
\end_layout

\begin_layout Plain Layout

	fmt.Println(Map(strs, strings.ToUpper))
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run collection-functions.go 
\end_layout

\begin_layout LyX-Code
2
\end_layout

\begin_layout LyX-Code
false
\end_layout

\begin_layout LyX-Code
true
\end_layout

\begin_layout LyX-Code
false
\end_layout

\begin_layout LyX-Code
[peach apple pear]
\end_layout

\begin_layout LyX-Code
[PEACH APPLE PEAR PLUM]
\end_layout

\begin_layout Section
Функции для работы со строками
\end_layout

\begin_layout Standard
Пакет 
\shape italic
strings
\shape default
 из стандартной библиотеки содержит множество функций, отвечающих за обработку
 строк.
 Ниже приведены примеры использования некоторых из них.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import s "strings"
\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Мы создали короткий алиас для fmt.Println, который
\end_layout

\begin_layout Plain Layout

//и будем использовать дальше
\end_layout

\begin_layout Plain Layout

var p = fmt.Println
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

//Ниже вы можете увидеть примеры функций из пакета
\end_layout

\begin_layout Plain Layout

//strings.
 Следует отметить, что все они именно функции,
\end_layout

\begin_layout Plain Layout

//а не методы для объекта строки.
 Это значит, что мы
\end_layout

\begin_layout Plain Layout

//должны передать функции в качестве первого аргумента
\end_layout

\begin_layout Plain Layout

//строку
\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//Содержит ли строка подстроку
\end_layout

\begin_layout Plain Layout

	p("Contains:  ", s.Contains("test", "es"))
\end_layout

\begin_layout Plain Layout

	//Сколько раз встречается подстрока
\end_layout

\begin_layout Plain Layout

	p("Count:     ", s.Count("test", "t"))
\end_layout

\begin_layout Plain Layout

	//Начинается ли строка с подстроки
\end_layout

\begin_layout Plain Layout

	p("HasPrefix: ", s.HasPrefix("test", "te"))
\end_layout

\begin_layout Plain Layout

	//Заканчивается ли строка подстрокой
\end_layout

\begin_layout Plain Layout

	p("HasSuffix: ", s.HasSuffix("test", "st"))
\end_layout

\begin_layout Plain Layout

	//Индекс подстроки в строке
\end_layout

\begin_layout Plain Layout

	p("Index:     ", s.Index("test", "e"))
\end_layout

\begin_layout Plain Layout

	//Объединение среза в строку
\end_layout

\begin_layout Plain Layout

	p("Join:      ", s.Join([]string{"a", "b"}, "-"))
\end_layout

\begin_layout Plain Layout

	//Повторение строки
\end_layout

\begin_layout Plain Layout

	p("Repeat:    ", s.Repeat("a", 5))
\end_layout

\begin_layout Plain Layout

	//Замена частей строки
\end_layout

\begin_layout Plain Layout

	p("Replace:   ", s.Replace("foo", "o", "0", -1))
\end_layout

\begin_layout Plain Layout

	p("Replace:   ", s.Replace("foo", "o", "0", 1))
\end_layout

\begin_layout Plain Layout

	//Разбивка строки на срез
\end_layout

\begin_layout Plain Layout

	p("Split:     ", s.Split("a-b-c-d-e", "-"))
\end_layout

\begin_layout Plain Layout

	//Изменение регистра
\end_layout

\begin_layout Plain Layout

	p("ToLower:   ", s.ToLower("TEST"))
\end_layout

\begin_layout Plain Layout

	p("ToUpper:   ", s.ToUpper("test"))
\end_layout

\begin_layout Plain Layout

	p()
\end_layout

\begin_layout Plain Layout

	
\end_layout

\begin_layout Plain Layout

	//Эти функции не являются частью пакета strings,
\end_layout

\begin_layout Plain Layout

	//но о них стоит упомянуть.
 Узнаём длину строки и
\end_layout

\begin_layout Plain Layout

	//получаем символ по его индексу
\end_layout

\begin_layout Plain Layout

	p("Len: ", len("hello"))
\end_layout

\begin_layout Plain Layout

	p("Char:", "hello"[1])
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run string-functions.go
\end_layout

\begin_layout LyX-Code
Contains: true
\end_layout

\begin_layout LyX-Code
Count: 2
\end_layout

\begin_layout LyX-Code
HasPrefix: true
\end_layout

\begin_layout LyX-Code
HasSuffix: true
\end_layout

\begin_layout LyX-Code
Index: 1
\end_layout

\begin_layout LyX-Code
Join: a-b
\end_layout

\begin_layout LyX-Code
Repeat: aaaaa
\end_layout

\begin_layout LyX-Code
Replace: f00
\end_layout

\begin_layout LyX-Code
Replace: f0o
\end_layout

\begin_layout LyX-Code
Split: [a b c d e]
\end_layout

\begin_layout LyX-Code
toLower: test
\end_layout

\begin_layout LyX-Code
ToUpper: TEST
\end_layout

\begin_layout LyX-Code

\end_layout

\begin_layout LyX-Code
Len: 5
\end_layout

\begin_layout LyX-Code
Char: 101
\end_layout

\begin_layout Standard
Больше информации о функциях для работы со строками можно найти в документации
 к пакету 
\shape italic
strings
\shape default
: 
\begin_inset CommandInset href
LatexCommand href
target "https://golang.org/pkg/strings/"

\end_inset

 (англ.).
\end_layout

\begin_layout Section
Форматирование строк
\end_layout

\begin_layout Standard
В Go содержится большое количество возможностей для форматирования строк
 в традиционном стиле 
\shape italic
printf
\shape default
.
 Ниже приведены несколько примеров решения подобных задач.
\end_layout

\begin_layout Standard
\begin_inset listings
inline false
status open

\begin_layout Plain Layout

package main
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

import "fmt"
\end_layout

\begin_layout Plain Layout

import "os"
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

type point struct {
\end_layout

\begin_layout Plain Layout

	x, y int
\end_layout

\begin_layout Plain Layout

}
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

func main() {
\end_layout

\begin_layout Plain Layout

	//В Go есть специальные "глаголы", которые специфицируют
\end_layout

\begin_layout Plain Layout

	//форматирование основных значений данных.
 Пример ниже
\end_layout

\begin_layout Plain Layout

	//выводит на печать образец структуры point
\end_layout

\begin_layout Plain Layout

	p := point{1, 2}
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%v
\backslash
n", p)
\end_layout

\begin_layout Plain Layout

	
\end_layout

\begin_layout Plain Layout

	//Используя %+v, можно вывести на печать также имена
\end_layout

\begin_layout Plain Layout

	//полей структуры
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%+v
\backslash
n", p)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//%#v же позволит напечатать синтаксическое
\end_layout

\begin_layout Plain Layout

	//представление этого значения, т.е.
 создавший
\end_layout

\begin_layout Plain Layout

	//его кусок кода
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%#v
\backslash
n", p)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Чтобы вывести тип данного значения, используйте
\end_layout

\begin_layout Plain Layout

	//%T
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%T
\backslash
n", p)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//С форматированием логических значений тоже нет
\end_layout

\begin_layout Plain Layout

	//ничего сложного
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%t
\backslash
n", true)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Существует множество способов форматировать целые
\end_layout

\begin_layout Plain Layout

	//числа.
 Чтобы вывести представление числа в десятичной
\end_layout

\begin_layout Plain Layout

	//системе счисления, используйте %d
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%d
\backslash
n", 123)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Представление в двоичной системе счисления
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%b
\backslash
n", 14)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Вывод символа Unicode, соответствующего данному числу
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%c
\backslash
n", 33)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Шестнадцатеричная система счисления
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%x
\backslash
n", 456)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Существует множество способов и для форматирования
\end_layout

\begin_layout Plain Layout

	//чисел с плавающей точкой.
 %f используется для
\end_layout

\begin_layout Plain Layout

	//десятичной формы
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%f
\backslash
n", 78.9)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//%e и %E с небольшими различиями форматируют числа
\end_layout

\begin_layout Plain Layout

	//в экспоненциальной записи
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%e
\backslash
n", 123400000.0)
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%E
\backslash
n", 123400000.0)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//%s предназначается для того, чтобы "просто вывести"
\end_layout

\begin_layout Plain Layout

	//строку
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%s
\backslash
n", "
\backslash
"string
\backslash
"")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Строка с двойными кавычками
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%q
\backslash
n", "
\backslash
"string
\backslash
"")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Как и в случае с целыми числами, %x используется для
\end_layout

\begin_layout Plain Layout

	//вывода строки в шестнадцатеричной форме, по два символа
\end_layout

\begin_layout Plain Layout

	//на каждый байт
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%x
\backslash
n", "hex this")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Вывод указателя
\end_layout

\begin_layout Plain Layout

	fmt.Printf("%p
\backslash
n", &p)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//При форматировании чисел нам зачастую требуется
\end_layout

\begin_layout Plain Layout

	//контролировать ширину и точность конечного
\end_layout

\begin_layout Plain Layout

	//результата.
 Указать ширину вывода целого числа
\end_layout

\begin_layout Plain Layout

	//можно, вставив значение ширины после %.
 По
\end_layout

\begin_layout Plain Layout

	//умолчанию будет использоваться выравнивание по
\end_layout

\begin_layout Plain Layout

	//правой стороне.
 Пустоты будут заполнены пробелами
\end_layout

\begin_layout Plain Layout

	fmt.Printf("|%6d|%6d|
\backslash
n", 12, 345)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Таким же образом можно указать ширину вывода чисел
\end_layout

\begin_layout Plain Layout

	//с плавающей точкой.
 Точность вывода можно указать в форме
\end_layout

\begin_layout Plain Layout

	//"ширина.количество-знаков-после-запятой"
\end_layout

\begin_layout Plain Layout

	fmt.Printf("|%6.2f|%6.2f|
\backslash
n", 1.2, 3.45)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Для выравнивания по левой стороне используется -
\end_layout

\begin_layout Plain Layout

	fmt.Printf("|%-6.2f|%-6.2f|
\backslash
n", 1.2, 3.45)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Ширину строк также можно контролировать (это может быть
\end_layout

\begin_layout Plain Layout

	//полезно в том случае, когда вы хотите вывести на печать
\end_layout

\begin_layout Plain Layout

	//таблицу).
 Ниже представлен пример с выравниванием по
\end_layout

\begin_layout Plain Layout

	//правой стороне
\end_layout

\begin_layout Plain Layout

	fmt.Printf("|%6s|%6s|
\backslash
n", "foo", "b")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Как и в случае с числами, - используется для выравнивания
\end_layout

\begin_layout Plain Layout

	//по левой стороне
\end_layout

\begin_layout Plain Layout

	fmt.Printf("|%-6s|%-6s|
\backslash
n", "foo", "b")
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Printf применяется для вывода в os.Stdout.
 Sprintf же
\end_layout

\begin_layout Plain Layout

	//форматирует и возвращает строку, не печатая её нигде
\end_layout

\begin_layout Plain Layout

	s := fmt.Sprintf("a %s", "string")
\end_layout

\begin_layout Plain Layout

	fmt.Println(s)
\end_layout

\begin_layout Plain Layout

\end_layout

\begin_layout Plain Layout

	//Чтобы форматировать и вывести на печать в другие потоки
\end_layout

\begin_layout Plain Layout

	//(io.Writers), используйте Fprintf
\end_layout

\begin_layout Plain Layout

	fmt.Fprintf(os.Stderr, "an %s
\backslash
n", "error")
\end_layout

\begin_layout Plain Layout

}
\end_layout

\end_inset


\end_layout

\begin_layout LyX-Code
$ go run string-formatting.go
\end_layout

\begin_layout LyX-Code
{1 2}
\end_layout

\begin_layout LyX-Code
{x:1 y:2}
\end_layout

\begin_layout LyX-Code
main.point{x:1, y:2}
\end_layout

\begin_layout LyX-Code
main.point
\end_layout

\begin_layout LyX-Code
true
\end_layout

\begin_layout LyX-Code
123
\end_layout

\begin_layout LyX-Code
1110
\end_layout

\begin_layout LyX-Code
!
\end_layout

\begin_layout LyX-Code
1c8
\end_layout

\begin_layout LyX-Code
78.900000
\end_layout

\begin_layout LyX-Code
1.234000e+08
\end_layout

\begin_layout LyX-Code
1.234000E+08
\end_layout

\begin_layout LyX-Code
"string"
\end_layout

\begin_layout LyX-Code
"
\backslash
"string
\backslash
""
\end_layout

\begin_layout LyX-Code
6865782074686973
\end_layout

\begin_layout LyX-Code
0x42135100
\end_layout

\begin_layout LyX-Code
| 12| 345|
\end_layout

\begin_layout LyX-Code
| 1.20| 3.45|
\end_layout

\begin_layout LyX-Code
|1.20 |3.45 |
\end_layout

\begin_layout LyX-Code
| foo| b|
\end_layout

\begin_layout LyX-Code
|foo |b |
\end_layout

\begin_layout LyX-Code
a string
\end_layout

\begin_layout LyX-Code
an error
\end_layout

\end_body
\end_document
